基础介绍
ES6 是 javascript 新的语法标准,从 2015 发布以来,已经得到浏览器广泛的支持, 具体的支持情况可以参考 caniuse 。本文主要介绍 ES6 新的语法特性,具体的内容参考自廖一峰老师的 ES6 标准入门 ,有兴趣的可以去买来支持一下。
新语法特性
ES6 新增了比较多的新语法特性,下面列举了其中最实用,使用最广泛的语法特性,对开发效率的提升还是很明显的。
let 和 const
在 ES5 中定义变量,一般会使用 var ,但是 var 定义的变量存在两个基本的问题:
- var 变量存在变量提升的问题,变量的定义会被提升至最前面,导致在定义前使用变量不会报错;
- var 变量欠缺块级作用域,变量要么属于全局作用域,要么属于函数作用域,块级作用域的缺失会导致隐藏一些异常的bug;
let 和 const 定义的变量修复了上面的问题,其中 let 与 var 类似,但是修复了上面的两个问题,const 可以用于定义不可变的常量。下面列举说明:
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
上面的代码中,a 就是具备块级作用域特性,在块外使用会报错,但是 b 则不具备块级作用域,在块外依旧可以使用。
在支持 ES6 的情况下建议使用 let 替代 var,而在不需要改变的情况下,尽量使用 const
变量解析赋值
在 ES6 支持变量解析,可以从数组或对象中解析出数据,赋值给变量。具体如下:
-
数组解析
使用数组赋值,可以将数组解析为一系列变量,使用如下所示:
let [a, b, c] = [1, 2, 3]; a // 1 b // 2 c // 3
可以看到将数组解析为变量a, b, c,可以方便地获取数组中值
在解析中支持默认值的使用,使用如下所示:
let [a, b = 2] = [1]; a // 1 b // 2
-
对象解析
对象也支持解析,可以将对象解析为一系列变量,对象的解析是根据属性解析的,因此变量名需要与属性相同,使用如下所示:
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; foo // "aaa" bar // "bbb"
解析构造还支持字符串,数值,布尔值,以及函数参数的解析赋值,有兴趣可以了解一下
模板字符串
很多语言都支持模板字符串,Python 也在 Python 3 中引入了模板字符串,javascript 也不甘落后,ES6 引入了比较便利的模板字符串语法,使用如下所示:
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
ES6 的模板字符串使用反引号 ` 包含模板字符串,其中使用符号 $ 包含变量。使用更加便利
函数扩展
在 ES6 中,对原有的函数进行了扩展, 支持了更多的新特性,具体如下:
-
参数默认值
在 ES5 中,函数不支持默认参数,只能通过
d = d || 'default'
的语法进行弥补,在 ES6 中支持了默认值,可以直接使用如下所示的写法了:function log(d = 'default') { console.log(d); }
-
rest 参数
在 ES6 中,支持可变长度的参数调用,引入了
...变量
用户获取多余的参数,使用如下所示:function log(a, ...b) { console.log(a); // 1 console.log(b); // [2, 3] } log(1, 2, 3);
-
箭头函数
在 ES6 中,引入了一种新的函数定义方式,箭头函数,可以简化函数的定义语法。使用如下:
var sum = (num1, num2) => { return num1 + num2; } // 等同于 var sum = function(num1, num2) { return num1 + num2; };
数组扩展
在 ES6 中,对原有的数组进行了扩展, 增加了比较多的新方法,同时支持使用扩展运算符 ...
将数组转换为参数序列,在函数调用时比较方便,具体如下:
var sum = (num1, num2) => { return num1 + num2; }
sum(...[1,2])
在上面的例子中就使用 ...[1,2]
将数组转换为 1, 2
参数序列
对象扩展
在 ES6 中,对象支持比较多的新特性,具体如下:
-
对象属性简写
在 ES6 中,对于同名的属性可以简写代替,具体如下面的例子:
const foo = 'bar'; const baz = {foo}; // {foo: 'bar'} // 等同于 const baz = {foo: foo};
即如果属性名与变量名相同,可以简写为单个变量的形式
-
对象解析赋值
前面介绍过对象支持解析赋值,对于没有没有被分配的属性,可以使用扩展运算符
...
进行分配存储。使用类似如下:let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x // 1 y // 2 z // { a: 3, b: 4 }
上面的例子中,由于对象的解析赋值,将对象的属性x, y 赋值给变量x, y,没有被分配的属性会被分配给 z
-
对象解构
类似与数组,可以将对象通过扩展运算符
…
取出所有的属性,转换至新对象中。使用类似如下所示:let x = { a: 1, b: 2 }; let y = { c: 3, d: 4 } let z = { ...x, ...y }; // { a: 1, b: 2, c: 3, d: 4}
Set 和 Map
Set 和 Map 是其他语言中最常见的数据结构,在 javascript 中一直缺失,在需要使用 Map 时,之前一直是使用对象代替,而 Set 就没有比较好的替代方案。在 ES6 中这两个基础的数据结构都被补上了,给后续使用带来了很多便利
Promise
Promise 是 ES6 中提供了便利的异步调用方案,可以将异步调用方法采用同步方式写出来,之前写过 Promise 异步调用 介绍相关的使用,就不重复介绍了,有兴趣的可以查看之前的文章。
迭代器遍历
ES6 引入了迭代器,为不同的数据提供了统一的数据遍历方式。与 Python 类似,ES6 的迭代器是通过反复调用数据的 next()
方法来获取单个数据的,因此,只要数据结构支持 next()
接口返回数据,就可以被遍历。
ES6 中提供了新的遍历访问方式,for... of
语法,此语法可以顺序遍历支持迭代器的数据结构。因此对于支持迭代访问的对象,都可以使用 for...of
访问。
默认支持迭代访问的常见数据结构为:
- Array
- Map
- Set
- String
- 函数的 arguments 对象
因此可以使用如下所示的方式访问数据:
var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (let [name, value] of es6) {
console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-262
Generator 生成器
ES6 提供了类似于 Python 中的生成器,对于 Python 迭代器的使用可以查看之前的 Python 优雅迭代器 ,类似于 Python 中的生成器,在 ES6 的方法中,通过 yield 语法将需要遍历的数据返回,后续可以迭代访问 yield 返回的数据。使用类似如下所示:
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
}
var hw = helloWorldGenerator();
for (let d of hw){
console.log(d);
}
// 'hello'
// 'world'
在上面的代码中,方法 helloWorldGenerator()
将需要返回的数据 ‘hello’ , ‘world’ 通过 yield 语句返回,之后可以迭代访问生成器方法 helloWorldGenerator()
,即可获取到相关的数据
async 和 await
async 和 await 是 ES6 中引入的异步语法,可以进一步优化异步方法调用的语法,使用更加类似通过的方式调用异步方法。
async 语法用于标识方法中包含异步操作
await 用于表示紧跟其后的语句需要等待结果,函数执行中碰到 await 语句就会先返回,等待异步操作结束才继续执行后续的语句
通过 async 与 await 方法的配合使用,可以将异步调用采用完全类似同步的方法写出来。具体的例子如下所示:
async function getStockPriceByName(name) {
const stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function (result) {
console.log(result);
});
上面的例子中,方法内部包含异步方法 getStockPrice()
, 通过 await 语句表示后续的返回值需要等待异步方法getStockPrice()
执行结束才会执行,这样保证可以得到异步操作的返回值。
在方法的外部使用 async 语句表示此方法getStockPriceByName()
中包含异步操作,这样调用者如果需要在获得此方法返回值才执行一些操作,可以使用 Promise 的 then 语法,或者使用 await 等待数据并展示。
Class 类
在之前的 javascript 中,为了实现类,一般是定义一个方法对象。类似如下所示:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
这种定义方式十分不清晰,令新手十分茫然,难以理解。在 ES6 中,实现了新的 Class 语法,可以实现类似其他语言的类定义,将上面的代码采用新的 Class 语法写出类似如下所示:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var p = new Point(1, 2);
类的定义会清晰比较多,更加易于理解。
模块引用
ES6 提供了新的模块引用方案,通过使用 export 和 import 进行模块的引用。在使用中通过 export 指定当前模块对外提供的接口,然后通过 import 引入其他模块提供的接口。
通过 export 提供接口的示例类似如下所示:
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
在需要引用当前模块的地方,通过 import 进行引用,使用类似如下所示:
import {firstName, lastName, year} from './profile.js';
总结
在上面的介绍中,基本覆盖了 ES6 中提供的实用的语法特性,但是在各个新的语法特性中,都还有更多的细节有待挖掘,如果有兴趣,可以进一步去探索 ES6 中各个语法特性中的细节。整体感觉下来,ES6 将 javascript 语言中一些最基础的痛点都补全了,让 javascript 更像一些完善的开发语言了,推荐大家都用起来吧。