ES6 是下一代 JavaScript 語(yǔ)法標(biāo)準(zhǔn),比起 ES5 有很大的變化。標(biāo)準(zhǔn)委員會(huì)每年發(fā)布一次當(dāng)年度的新標(biāo)準(zhǔn),目前最新的標(biāo)準(zhǔn)是《ES2018 標(biāo)準(zhǔn)》,因此 ES6 是一個(gè)動(dòng)態(tài)的標(biāo)準(zhǔn),每年都會(huì)發(fā)生一些變化。
React 大量使用 ES6 語(yǔ)法,本文介紹其中一些最重要的語(yǔ)法點(diǎn)。完整的 ES6 介紹請(qǐng)參考 http://es6.ruanyifeng.com/。
let 和 const
let和const命令用于聲明變量。
let聲明的變量是可變的,const聲明的變量是不可變的。
let foo = 1;
foo = 2;
const bar = 1;
bar = 2; // 報(bào)錯(cuò)
上面代碼中,let聲明的變量foo是可以重新賦值,但是如果對(duì)bar聲明的變量重新賦值,就會(huì)報(bào)錯(cuò)。
注意,如果const聲明的變量指向一個(gè)對(duì)象,那么該對(duì)象的屬性是可變的。
const foo = {
bar: 1
};
foo.bar = 2;
上面代碼中,變量foo本身是不可變的,即foo不能指向另一個(gè)對(duì)象。但是,對(duì)象內(nèi)部的屬性是可變的,這是因?yàn)檫@時(shí)foo保存的是一個(gè)指針,這個(gè)指針本身不可變,但它指向的對(duì)象本身是可變的。
解構(gòu)賦值
ES6 允許按照一定模式,從數(shù)組和對(duì)象中提取值,對(duì)變量進(jìn)行賦值,這被稱為解構(gòu)(Destructuring)。
let [a, b, c] = [1, 2, 3];
上面代碼表示,可以從數(shù)組中提取值,按照對(duì)應(yīng)位置,對(duì)變量賦值。
解構(gòu)賦值不僅可以用于數(shù)組,還可以用于對(duì)象。
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
解構(gòu)賦值時(shí),還可以設(shè)置默認(rèn)值。
let [x, y = 'b'] = ['a']; // x='a', y='b'
上面代碼中,變量y解構(gòu)賦值時(shí)沒(méi)有取到值,所以默認(rèn)值就生效了。
對(duì)象的簡(jiǎn)潔表示法
ES6 允許直接寫(xiě)入變量和函數(shù),作為對(duì)象的屬性和方法。這樣的書(shū)寫(xiě)更加簡(jiǎn)潔。
const foo = 'bar';
const baz = { foo };
baz // {foo: "bar"}
除了屬性簡(jiǎn)寫(xiě),方法也可以簡(jiǎn)寫(xiě)。
const o = {
method() {
return "Hello!";
}
};
// 等同于
const o = {
method: function() {
return "Hello!";
}
};
箭頭函數(shù)
ES6 允許使用“箭頭”(=>)定義函數(shù)。
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
如果箭頭函數(shù)不需要參數(shù)或需要多個(gè)參數(shù),就使用一個(gè)圓括號(hào)代表參數(shù)部分。
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
如果箭頭函數(shù)的代碼塊部分多于一條語(yǔ)句,就要使用大括號(hào)將它們括起來(lái),并且使用return語(yǔ)句返回。
var sum = (num1, num2) => { return num1 + num2; }
rest 參數(shù)
ES6 引入 rest 參數(shù)(形式為...變量名),用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments對(duì)象了。rest 參數(shù)搭配的變量是一個(gè)數(shù)組,該變量將多余的參數(shù)放入數(shù)組中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
上面代碼的add函數(shù)是一個(gè)求和函數(shù),利用 rest 參數(shù),可以向該函數(shù)傳入任意數(shù)目的參數(shù)。
擴(kuò)展運(yùn)算符
擴(kuò)展運(yùn)算符(spread)是三個(gè)點(diǎn)(...)。它好比 rest 參數(shù)的逆運(yùn)算,將一個(gè)數(shù)組轉(zhuǎn)為用逗號(hào)分隔的參數(shù)序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
對(duì)象也可以使用擴(kuò)展運(yùn)算符。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
class
ES6 允許新建“類”(class)。
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
get boneCount() {
return this.bones.length;
}
set matrixType(matrixType) {
this.idMatrix = SkinnedMesh[matrixType]();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}
上面是一個(gè)類的定義。
- constructor():構(gòu)造函數(shù),新建實(shí)例的時(shí)候,自動(dòng)調(diào)用這個(gè)方法。
- extends:第一行的
extends關(guān)鍵字表示繼承某個(gè)父類。 - super:子類方法里面的
super指代父類。 - get():
get是取值器,讀取該方法定義的屬性時(shí),會(huì)自動(dòng)執(zhí)行指定的代碼。 - set():
set是賦值器,賦值該方法定義的屬性時(shí),會(huì)自動(dòng)執(zhí)行指定的代碼。 - static:方法前面加上
static關(guān)鍵字,表示該方法是靜態(tài)方法,定義在類上面,而不是定義在實(shí)例對(duì)象上面,以上面為例,就是SkinnedMesh.defaultMatrix()這樣調(diào)用。
定義了類以后,就可以新建實(shí)例了。
const instance = new SkinnedMesh();
Promise 對(duì)象
Promise 是 ES6 引入的封裝異步操作的統(tǒng)一接口。它返回一個(gè)對(duì)象,包含了異步操作的信息。
Promise 本身是一個(gè)構(gòu)造函數(shù),提供了resolve和reject兩個(gè)方法。一旦異步操作成功結(jié)束,就調(diào)用resolve方法,將 Promise 實(shí)例對(duì)象的狀態(tài)改為resolved,一旦異步操作失敗,就調(diào)用reject方法,將 Promise 實(shí)例的狀態(tài)改成rejected。
function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
})
}
上面代碼中,timeout函數(shù)返回一個(gè) Promise 實(shí)例,在指定時(shí)間以后,將狀態(tài)改為resolved。
var p = timeout(1000).then(() => {
return timeout(2000);
}).then(() => {
throw new Error("hmm");
}).catch(err => {
return Promise.all([timeout(100), timeout(200)]);
})
一旦 Promise 實(shí)例的狀態(tài)改變以后,就可以使用then()方法指定下面將要執(zhí)行的函數(shù),catch()方法用來(lái)處理rejected狀態(tài)的情況。
module
ES6 意義最重大的語(yǔ)法變化,就是引入了模塊(module)。
一個(gè)模塊內(nèi)部,使用export命令輸出對(duì)外的接口。
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
上面的模塊輸出了sum和pi兩個(gè)接口。
import命令用于引入該模塊。
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
上面代碼中,*表示引入所有接口,也可以只引入指定的接口。
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));