?
This document uses PHP Chinese website manual Release
概述
使用ECMAScript 6的方法
數(shù)據(jù)類型
let命令
const命令
Set數(shù)據(jù)結(jié)構(gòu)
Map數(shù)據(jù)結(jié)構(gòu)
rest(...)運(yùn)算符
遍歷器(Iterator)
generator 函數(shù)
原生對(duì)象的擴(kuò)展
語法糖
二進(jìn)制和八進(jìn)制表示法
增強(qiáng)的對(duì)象寫法
箭頭函數(shù)(arrow)
函數(shù)參數(shù)的默認(rèn)值
模板字符串
for...of循環(huán)
數(shù)組推導(dǎo)
多變量賦值
數(shù)據(jù)結(jié)構(gòu)
class結(jié)構(gòu)
module定義
ECMAScript 7
參考鏈接
ECMAScript 6 是JavaScript的下一代標(biāo)準(zhǔn),正處在快速開發(fā)之中,大部分已經(jīng)完成了,預(yù)計(jì)將在2014年正式發(fā)布。Mozilla將在這個(gè)標(biāo)準(zhǔn)的基礎(chǔ)上,推出JavaScript 2.0。
ECMAScript 6的目標(biāo),是使得JavaScript可以用來編寫復(fù)雜的應(yīng)用程序、函數(shù)庫和代碼的自動(dòng)生成器(code generator)。
最新的瀏覽器已經(jīng)部分支持ECMAScript 6 的語法,可以通過《ECMAScript 6 瀏覽器兼容表》查看瀏覽器支持情況。
下面對(duì)ECMAScript 6新增的語法特性逐一介紹。由于ECMAScript 6的正式標(biāo)準(zhǔn)還未出臺(tái),所以以下內(nèi)容隨時(shí)可能發(fā)生變化,不一定是最后的版本。
目前,V8引擎已經(jīng)部署了ECMAScript 6的部分特性。使用node.js 0.11版,就可以體驗(yàn)這些特性。
node.js 0.11版的一種比較方便的使用方法,是使用版本管理工具nvm。下載nvm以后,進(jìn)入項(xiàng)目目錄,運(yùn)行下面的命令,激活nvm。
source nvm.sh
然后,指定node運(yùn)行版本。
nvm use 0.11
最后,用--harmony參數(shù)進(jìn)入node運(yùn)行環(huán)境,就可以在命令行下體驗(yàn)ECMAScript 6了。
node --harmony
另外,可以使用Google的Traceur(在線轉(zhuǎn)換工具),將ES6代碼編譯為ES5。
# 安裝 npm install -g traceur # 運(yùn)行ES6文件 traceur /path/to/es6 # 將ES6文件轉(zhuǎn)為ES5文件 traceur --script /path/to/es6 --out /path/to/es5
(1)概述
ECMAScript 6新增了let命令,用來聲明變量。它的用法類似于var,但是所聲明的變量,只在let命令所在的代碼塊內(nèi)有效。
{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b //1
上面代碼在代碼塊之中,分別用let和var聲明了兩個(gè)變量。然后在代碼塊之外調(diào)用這兩個(gè)變量,結(jié)果let聲明的變量報(bào)錯(cuò),var聲明的變量返回了正確的值。這表明,let聲明的變量只在它所在的代碼塊有效。
下面的代碼如果使用var,最后輸出的是9。
var a = []; for (var i = 0; i < 10; i++) { var c = i; a[i] = function () { console.log(c); }; } a[6](); // 9
如果使用let,聲明的變量?jī)H在塊級(jí)作用域內(nèi)有效,最后輸出的是6。
var a = []; for (var i = 0; i < 10; i++) { let c = i; a[i] = function () { console.log(c); }; } a[6](); // 6
注意,let不允許在相同作用域內(nèi),重復(fù)聲明同一個(gè)變量。
// 報(bào)錯(cuò) { let a = 10; var a = 1; } // 報(bào)錯(cuò) { let a = 10; let a = 1; }
(2)塊級(jí)作用域
let實(shí)際上為JavaScript新增了塊級(jí)作用域。
function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 }
上面的函數(shù)有兩個(gè)代碼塊,都聲明了變量n,運(yùn)行后輸出5。這表示外層代碼塊不受內(nèi)層代碼塊的影響。如果使用var定義變量n,最后輸出的值就是10。
塊級(jí)作用域的出現(xiàn),實(shí)際上使得獲得廣泛應(yīng)用的立即執(zhí)行函數(shù)(IIFE)不再必要了。
// IIFE寫法 (function () { var tmp = ...; ... }()); // 塊級(jí)作用域?qū)懛? { let tmp = ...; ... }
(3)不存在變量提升
需要注意的是,let聲明的變量不存在“變量提升”現(xiàn)象。
console.log(x); let x = 10;
上面代碼運(yùn)行后會(huì)報(bào)錯(cuò),表示x沒有定義。如果用var聲明x,就不會(huì)報(bào)錯(cuò),輸出結(jié)果為undefined。
const也用來聲明變量,但是聲明的是常量。一旦聲明,常量的值就不能改變。
const PI = 3.1415; PI // 3.1415 PI = 3; PI // 3.1415 const PI = 3.1; PI // 3.1415
上面代碼表明改變常量的值是不起作用的。需要注意的是,對(duì)常量重新賦值不會(huì)報(bào)錯(cuò),只會(huì)默默地失敗。
const的作用域與var命令相同:如果在全局環(huán)境聲明,常量就在全局環(huán)境有效;如果在函數(shù)內(nèi)聲明,常量就在函數(shù)體內(nèi)有效。
ES6提供了新的數(shù)據(jù)結(jié)構(gòu)Set。它類似于數(shù)組,但是成員的值都是唯一的,沒有重復(fù)的值。
Set本身是一個(gè)構(gòu)造函數(shù),用來生成Set數(shù)據(jù)結(jié)構(gòu)。
var s = new Set(); [2,3,5,4,5,2,2].map(x => s.add(x)) for (i of s) {console.log(i)} // 2 3 4 5
上面代碼表示,set數(shù)據(jù)結(jié)構(gòu)不會(huì)添加重復(fù)的值。
set數(shù)據(jù)結(jié)構(gòu)有以下屬性和方法:
size:返回成員總數(shù)。
add(value):添加某個(gè)值。
delete(value):刪除某個(gè)值。
has(value):返回一個(gè)布爾值,表示該值是否為set的成員。
clear():清除所有成員。
s.add("1").add("2").add("2"); // 注意“2”被加入了兩次 s.size // 2 s.has("1") // true s.has("2") // true s.has("3") // false s.delete("2"); s.has("2") // false
ES6還提供了map數(shù)據(jù)結(jié)構(gòu)。它類似于對(duì)象,就是一個(gè)鍵值對(duì)的集合,但是“鍵”的范圍不限于字符串,甚至對(duì)象也可以當(dāng)作鍵。
var m = new Map(); o = {p: "Hello World"}; m.set(o, "content") console.log(m.get(o)) // "content"
上面代碼將一個(gè)對(duì)象當(dāng)作m的一個(gè)鍵。
Map數(shù)據(jù)結(jié)構(gòu)有以下屬性和方法。
size:返回成員總數(shù)。
set(key, value):設(shè)置一個(gè)鍵值對(duì)。
get(key):讀取一個(gè)鍵。
has(key):返回一個(gè)布爾值,表示某個(gè)鍵是否在Map數(shù)據(jù)結(jié)構(gòu)中。
delete(key):刪除某個(gè)鍵。
clear():清除所有成員。
var m = new Map(); m.set("edition", 6) // 鍵是字符串 m.set(262, "standard") // 鍵是數(shù)值 m.set(undefined, "nah") // 鍵是undefined var hello = function() {console.log("hello");} m.set(hello, "Hello ES6!") // 鍵是函數(shù) m.has("edition") // true m.has("years") // false m.has(262) // true m.has(undefined) // true m.has(hello) // true m.delete(undefined) m.has(undefined) // false m.get(hello) // Hello ES6! m.get("edition") // 6
(1)基本用法
ES6引入rest運(yùn)算符(...),用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments.length了。rest運(yùn)算符后面是一個(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運(yùn)算符,可以向該函數(shù)傳入任意數(shù)目的參數(shù)。
下面是一個(gè)利用rest運(yùn)算符改寫數(shù)組push方法的例子。
function push(array, ...items) { items.forEach(function(item) { array.push(item); console.log(item); }); } var a = []; push(a, "a1", "a2", "a3", "a4");
(2)將數(shù)組轉(zhuǎn)為參數(shù)序列
rest運(yùn)算符不僅可以用于函數(shù)定義,還可以用于函數(shù)調(diào)用。
function f(s1, s2, s3, s4, s5) { console.log(s1 + s2 + s3 + s4 +s5); } var a = ["a2", "a3", "a4", "a5"]; f("a1", ...a) // a1a2a3a4a5
從上面的例子可以看出,rest運(yùn)算符的另一個(gè)重要作用是,可以將數(shù)組轉(zhuǎn)變成正常的參數(shù)序列。利用這一點(diǎn),可以簡(jiǎn)化求出一個(gè)數(shù)組最大元素的寫法。
// ES5 Math.max.apply(null, [14, 3, 77]) // ES6 Math.max(...[14, 3, 77]) // 等同于 Math.max(14, 3, 77);
上面代碼表示,由于JavaScript不提供求數(shù)組最大元素的函數(shù),所以只能套用Math.max函數(shù),將數(shù)組轉(zhuǎn)為一個(gè)參數(shù)序列,然后求最大值。有了rest運(yùn)算符以后,就可以直接用Math.max了。
遍歷器(Iterator)是一種協(xié)議,任何對(duì)象都可以部署遍歷器協(xié)議,從而使得for...of循環(huán)可以遍歷這個(gè)對(duì)象。
遍歷器協(xié)議規(guī)定,任意對(duì)象只要部署了next方法,就可以作為遍歷器,但是next方法必須返回一個(gè)包含value和done兩個(gè)屬性的對(duì)象。其中,value屬性當(dāng)前遍歷位置的值,done屬性是一個(gè)布爾值,表示遍歷是否結(jié)束。
function makeIterator(array){ var nextIndex = 0; return { next: function(){ return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {done: true}; } } } var it = makeIterator(['a', 'b']); it.next().value // 'a' it.next().value // 'b' it.next().done // true
下面是一個(gè)無限運(yùn)行的遍歷器的例子。
function idMaker(){ var index = 0; return { next: function(){ return {value: index++, done: false}; } } } var it = idMaker(); it.next().value // '0' it.next().value // '1' it.next().value // '2' // ...
上一部分的遍歷器,用來依次取出集合中的每一個(gè)成員,但是某些情況下,我們需要的是一個(gè)內(nèi)部狀態(tài)的遍歷器。也就是說,每調(diào)用一次遍歷器,對(duì)象的內(nèi)部狀態(tài)發(fā)生一次改變(可以理解成發(fā)生某些事件)。ECMAScript 6 引入了generator函數(shù),作用就是返回一個(gè)內(nèi)部狀態(tài)的遍歷器,主要特征是函數(shù)內(nèi)部使用了yield語句。
當(dāng)調(diào)用generator函數(shù)的時(shí)候,該函數(shù)并不執(zhí)行,而是返回一個(gè)遍歷器(可以理解成暫停執(zhí)行)。以后,每次調(diào)用這個(gè)遍歷器的next方法,就從函數(shù)體的頭部或者上一次停下來的地方開始執(zhí)行(可以理解成恢復(fù)執(zhí)行),直到遇到下一個(gè)yield語句為止,并返回該yield語句的值。
ECMAScript 6草案定義的generator函數(shù),需要在function關(guān)鍵字后面,加一個(gè)星號(hào)。然后,函數(shù)內(nèi)部使用yield語句,定義遍歷器的每個(gè)成員。
function* helloWorldGenerator() { yield 'hello'; yield 'world'; }
yield有點(diǎn)類似于return語句,都能返回一個(gè)值。區(qū)別在于每次遇到y(tǒng)ield,函數(shù)返回緊跟在yield后面的那個(gè)表達(dá)式的值,然后暫停執(zhí)行,下一次從該位置繼續(xù)向后執(zhí)行,而return語句不具備位置記憶的功能。
上面代碼定義了一個(gè)generator函數(shù)helloWorldGenerator,它的遍歷器有兩個(gè)成員“hello”和“world”。調(diào)用這個(gè)函數(shù),就會(huì)得到遍歷器。
var hw = helloWorldGenerator();
執(zhí)行遍歷器的next方法,則會(huì)依次遍歷每個(gè)成員。
hw.next() // { value: 'hello', done: false } hw.next() // { value: 'world', done: false } hw.next() // { value: undefined, done: true } hw.next() // Error: Generator has already finished // at GeneratorFunctionPrototype.next (native) // at repl:1:3 // at REPLServer.defaultEval (repl.js:129:27) // ...
上面代碼一共調(diào)用了四次next方法。
第一次調(diào)用:函數(shù)開始執(zhí)行,直到遇到第一句yield語句為止。next方法返回一個(gè)對(duì)象,它的value屬性就是當(dāng)前yield語句的值hello,done屬性的值false,表示遍歷還沒有結(jié)束。
第二次調(diào)用:函數(shù)從上次yield語句停下的地方,一直執(zhí)行到下一個(gè)yield語句。next方法返回一個(gè)對(duì)象,它的value屬性就是當(dāng)前yield語句的值world,done屬性的值false,表示遍歷還沒有結(jié)束。
第三次調(diào)用:函數(shù)從上次yield語句停下的地方,一直執(zhí)行到函數(shù)結(jié)束。next方法返回一個(gè)對(duì)象,它的value屬性就是函數(shù)最后的返回值,由于上例的函數(shù)沒有return語句(即沒有返回值),所以value屬性的值為undefined,done屬性的值true,表示遍歷已經(jīng)結(jié)束。
第四次調(diào)用:由于此時(shí)函數(shù)已經(jīng)運(yùn)行完畢,next方法直接拋出一個(gè)錯(cuò)誤。
遍歷器的本質(zhì),其實(shí)是使用yield語句暫停執(zhí)行它后面的操作,當(dāng)調(diào)用next方法時(shí),再繼續(xù)往下執(zhí)行,直到遇到下一個(gè)yield語句,并返回該語句的值,如果直到運(yùn)行結(jié)束。
如果next方法帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield語句的返回值。
function* f() { for(var i=0; true; i++) { var reset = yield i; if(reset) { i = -1; } } } var g = f(); g.next() // { value: 0, done: false } g.next() // { value: 1, done: false } g.next(true) // { value: 0, done: false }
上面代碼先定義了一個(gè)可以無限運(yùn)行的generator函數(shù)f,如果next方法沒有參數(shù),正常情況下返回一個(gè)遞增的i;如果next方法有參數(shù),則上一次yield語句的返回值將會(huì)等于該參數(shù)。如果該參數(shù)為true,則會(huì)重置i的值。
generator函數(shù)的這種暫停執(zhí)行的效果,意味著可以把異步操作寫在yield語句里面,等到調(diào)用next方法時(shí)再往后執(zhí)行。這實(shí)際上等同于不需要寫回調(diào)函數(shù)了,因?yàn)楫惒讲僮鞯暮罄m(xù)操作可以放在yield語句下面,反正要等到調(diào)用next方法時(shí)再執(zhí)行。所以,generator函數(shù)的一個(gè)重要實(shí)際意義就是用來處理異步操作,改寫回調(diào)函數(shù)。
function* loadUI() { showLoadingScreen(); yield loadUIDataAsynchronously(); hideLoadingScreen(); }
上面代碼表示,第一次調(diào)用loadUI函數(shù)時(shí),該函數(shù)不會(huì)執(zhí)行,僅返回一個(gè)遍歷器。下一次對(duì)該遍歷器調(diào)用next方法,則會(huì)顯示登錄窗口,并且異步加載數(shù)據(jù)。再一次使用next方法,則會(huì)隱藏登錄窗口??梢钥吹?,這種寫法的好處是所有登錄窗口的邏輯,都被封裝在一個(gè)函數(shù),按部就班非常清晰。
下面是一個(gè)利用generator函數(shù),實(shí)現(xiàn)斐波那契數(shù)列的例子。
function* fibonacci() { var previous = 0, current = 1; while (true) { var temp = previous; previous = current; current = temp + current; yield current; } } for (var i of fibonacci()) { console.log(i); } // 1, 2, 3, 5, 8, 13, ...,
下面是利用for...of語句,對(duì)斐波那契數(shù)列的另一種實(shí)現(xiàn)。
function* fibonacci() { let [prev, curr] = [0, 1]; for (;;) { [prev, curr] = [curr, prev + curr]; yield curr; } } for (n of fibonacci()) { if (n > 1000) break; console.log(n); }
從上面代碼可見,使用for...of語句時(shí)不需要使用next方法。
這里需要注意的是,yield語句運(yùn)行的時(shí)候是同步運(yùn)行,而不是異步運(yùn)行(否則就失去了取代回調(diào)函數(shù)的設(shè)計(jì)目的了)。實(shí)際操作中,一般讓yield語句返回Promises對(duì)象。
var Q = require('q'); function delay(milliseconds) { var deferred = Q.defer(); setTimeout(deferred.resolve, milliseconds); return deferred.promise; } function *f(){ yield delay(100); };
上面代碼yield語句返回的就是一個(gè)Promises對(duì)象。
如果有一系列任務(wù)需要全部完成后,才能進(jìn)行下一步操作,yield語句后面可以跟一個(gè)數(shù)組。下面就是一個(gè)例子。
function *f() { var urls = [ 'http://example.com/', 'http://twitter.com/', 'http://bbc.co.uk/news/' ]; var arrayOfPromises = urls.map(someOperation); var arrayOfResponses = yield arrayOfPromises; this.body = "Results"; for (var i = 0; i < urls.length; i++) { this.body += '\n' + urls[i] + ' response length is ' + arrayOfResponses[i].body.length; } };
ES6對(duì)JavaScript的原生對(duì)象,進(jìn)行了擴(kuò)展,提供了一系列新的屬性和方法。
Number.EPSILON Number.isInteger(Infinity) // false Number.isNaN("NaN") // false Math.acosh(3) // 1.762747174039086 Math.hypot(3, 4) // 5 Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2 "abcde".contains("cd") // true "abc".repeat(3) // "abcabcabc" Array.from(document.querySelectorAll('*')) // Returns a real Array Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior [0, 0, 0].fill(7, 1) // [0,7,7] [1,2,3].findIndex(x => x == 2) // 1 ["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"] ["a", "b", "c"].keys() // iterator 0, 1, 2 ["a", "b", "c"].values() // iterator "a", "b", "c" Object.assign(Point, { origin: new Point(0,0) })
ECMAScript 6提供了很多JavaScript語法的便捷寫法。
ES6提供了二進(jìn)制和八進(jìn)制數(shù)值的新的寫法,分別用前綴0b和0o表示。
0b111110111 === 503 // true0o767 === 503 // true
ES6允許直接寫入變量和函數(shù),作為對(duì)象的屬性和方法。這樣的書寫更加簡(jiǎn)潔。
var Person = { name: '張三', //等同于birth: birth birth, // 等同于hello: function ()... hello() { console.log('我的名字是', this.name); } };
(1)定義
ES6允許使用“箭頭”(=>)定義函數(shù)。
var f = v => v;
上面的箭頭函數(shù)等同于:
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ù)的代碼塊部分多于一條語句,就要使用大括號(hào)將它們括起來,并且使用return語句返回。
var sum = (num1, num2) => { return num1 + num2; }
由于大括號(hào)被解釋為代碼塊,所以如果箭頭函數(shù)直接返回一個(gè)對(duì)象,必須在對(duì)象外面加上括號(hào)。
var getTempItem = id => ({ id: id, name: "Temp" });
(2)實(shí)例:回調(diào)函數(shù)的簡(jiǎn)化
箭頭函數(shù)的一個(gè)用處是簡(jiǎn)化回調(diào)函數(shù)。
// 正常函數(shù)寫法 [1,2,3].map(function (x) { return x * x; }); // 箭頭函數(shù)寫法 [1,2,3].map(x => x * x);
另一個(gè)例子是
// 正常函數(shù)寫法 var result = values.sort(function(a, b) { return a - b; }); // 箭頭函數(shù)寫法 var result = values.sort((a, b) => a - b);
(3)注意點(diǎn)
箭頭函數(shù)有幾個(gè)使用注意點(diǎn)。
函數(shù)體內(nèi)的this對(duì)象,綁定定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。
不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用new命令,否則會(huì)拋出一個(gè)錯(cuò)誤。
不可以使用arguments對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。
關(guān)于this對(duì)象,下面的代碼將它綁定定義時(shí)的對(duì)象。
var handler = { id: "123456", init: function() { document.addEventListener("click", event => this.doSomething(event.type), false); }, doSomething: function(type) { console.log("Handling " + type + " for " + this.id); } };
上面代碼的init和doSomething方法中,都使用了箭頭函數(shù),它們中的this都綁定handler對(duì)象。否則,doSomething方法內(nèi)部的this對(duì)象就指向全局對(duì)象,運(yùn)行時(shí)會(huì)報(bào)錯(cuò)。
ECMAScript 6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值。
function Point(x = 0, y = 0) { this.x = x; this.y = y; } var p = new Point(); // p = { x:0, y:0 }
模板字符串(template string)是增強(qiáng)版的字符串,即可以當(dāng)作普通字符串使用,也可以在字符串中嵌入變量。它用反引號(hào)(`)標(biāo)識(shí)。
// 普通字符串 `In JavaScript '\n' is a line-feed.` // 多行字符串 `In JavaScript this is not legal.` // 字符串中嵌入變量 var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?` var x = 1; var y = 2; console.log(`${ x } + ${ y } = ${ x + y}`) // "1 + 2 = 3"
JavaScript原有的for...in循環(huán),只能獲得對(duì)象的鍵名,不能直接獲取鍵值。ES6提供for...of循環(huán),允許遍歷獲得鍵值。
var arr = ["a", "b", "c", "d"]; for (a in arr) { console.log(a); } // 0 // 1 // 2 // 3 for (a of arr) { console.log(a); } // a // b // c // d
上面代碼表明,for...in循環(huán)讀取鍵名,for...of循環(huán)讀取鍵值。
for...of循環(huán)還可以遍歷對(duì)象。
var es6 = { edition: 6, committee: "TC39", standard: "ECMA-262" }; for (e in es6) { console.log(e); } // edition // committee // standard var engines = Set(["Gecko", "Trident", "Webkit", "Webkit"]); for (var e of engines) { console.log(e); } // Gecko // Trident // Webkit var es6 = new Map(); es6.set("edition", 6); es6.set("committee", "TC39"); es6.set("standard", "ECMA-262"); for (var [name, value] of es6) { console.log(name + ": " + value); } // edition: 6 // committee: TC39 // standard: ECMA-262
上面代碼一共包含三個(gè)例子,第一個(gè)是for...in循環(huán)的例子,后兩個(gè)是for...of循環(huán)的例子。最后一個(gè)例子是同時(shí)遍歷對(duì)象的鍵名和鍵值。
(1)基本用法
ES6提供簡(jiǎn)潔寫法,允許直接通過現(xiàn)有數(shù)組生成新數(shù)組,這被稱為數(shù)組推導(dǎo)(array comprehension)。
var a1 = [1, 2, 3, 4]; var a2 = [i * 2 for (i of a1)]; a2 // [2, 4, 6, 8]
上面代碼表示,通過for...of結(jié)構(gòu),數(shù)組a2直接在a1的基礎(chǔ)上生成。
數(shù)組推導(dǎo)可以替代map和filter方法。
[for (i of [1, 2, 3]) i * i]; // 等價(jià)于 [1, 2, 3].map(function (i) { return i * i }); [i for (i of [1,4,2,3,-8]) if (i < 3)]; // 等價(jià)于 [1,4,2,3,-8].filter(function(i) { return i < 3 });
上面代碼說明,模擬map功能只要單純的for...of循環(huán)就行了,模擬filter功能除了for...of循環(huán),還必須加上if語句。
(2)多重推導(dǎo)
新引入的for...of結(jié)構(gòu),可以直接跟在表達(dá)式的前面或后面,甚至可以在一個(gè)數(shù)組推導(dǎo)中,使用多個(gè)for...of結(jié)構(gòu)。
var a1 = ["x1", "y1"]; var a2 = ["x2", "y2"]; var a3 = ["x3", "y3"]; [(console.log(s + w + r)) for (s of a1) for (w of a2) for (r of a3)]; // x1x2x3 // x1x2y3 // x1y2x3 // x1y2y3 // y1x2x3 // y1x2y3 // y1y2x3 // y1y2y3
上面代碼在一個(gè)數(shù)組推導(dǎo)之中,使用了三個(gè)for...of結(jié)構(gòu)。
需要注意的是,數(shù)組推導(dǎo)的方括號(hào)構(gòu)成了一個(gè)單獨(dú)的作用域,在這個(gè)方括號(hào)中聲明的變量類似于使用let語句聲明的變量。
(3)字符串推導(dǎo)
由于字符串可以視為數(shù)組,因此字符串也可以直接用于數(shù)組推導(dǎo)。
[c for (c of 'abcde') if (/[aeiou]/.test(c))].join('') // 'ae' [c+'0' for (c of 'abcde')].join('') // 'a0b0c0d0e0'
上面代碼使用了數(shù)組推導(dǎo),對(duì)字符串進(jìn)行處理。
上一部分的數(shù)組推導(dǎo)有一個(gè)缺點(diǎn),就是新數(shù)組會(huì)立即在內(nèi)存中生成。這時(shí),如果原數(shù)組是一個(gè)很大的數(shù)組,將會(huì)非常耗費(fèi)內(nèi)存。
ES6允許簡(jiǎn)潔地對(duì)多變量賦值。正常情況下,將數(shù)組元素賦值給多個(gè)變量,只能一次次分開賦值。
var a = 1; var b = 2; var c = 3;
ES6允許寫成下面這樣。
var [a, b, c] = [1, 2, 3];
本質(zhì)上,這種寫法屬于模式匹配,只要等號(hào)兩邊的模式相同,左邊的變量就會(huì)被賦予對(duì)應(yīng)的值。下面是一些嵌套數(shù)組的例子。
var [foo, [[bar], baz]] = [1, [[2], 3]] var [,,third] = ["foo", "bar", "baz"] var [head, ...tail] = [1, 2, 3, 4]
它還可以接受默認(rèn)值。
var [missing = true] = []; console.log(missing) // true var { x = 3 } = {}; console.log(x) // 3
它不僅可以用于數(shù)組,還可以用于對(duì)象。
var { foo, bar } = { foo: "lorem", bar: "ipsum" }; foo // "lorem" bar // "ipsum" var o = { p1: [ "Hello", { p2: "World" } ] }; var { a: [p1, { p2 }] } = o; console.log(p1) // "Hello" console.log(p2) // "World"
這種寫法的用途很多。
(1)交換變量的值。
[x, y] = [y, x];
(2)從函數(shù)返回多個(gè)值。
function example() { return [1, 2, 3]; } var [a, b, c] = example();
(3)函數(shù)參數(shù)的定義。
function f({p1, p2, p3}) { // ... }
(4)函數(shù)參數(shù)的默認(rèn)值。
jQuery.ajax = function (url, { async = true, beforeSend = function () {}, cache = true, complete = function () {}, crossDomain = false, global = true, // ... more config }) { // ... do stuff };
(1)基本用法
ES6提供了“類”(class)。此前,一般用構(gòu)造函數(shù)模擬“類”。
// ES5 var Language = function(config) { this.name = config.name; this.founder = config.founder; this.year = config.year; }; Language.prototype.summary = function() { return this.name+"由"+this.founder+"在"+this.year+"創(chuàng)造"; }; // ES6 class Language { constructor(name, founder, year) { this.name = name; this.founder = founder; this.year = year; } summary() { return this.name+"由"+this.founder+"在"+this.year+"創(chuàng)造"; } }
在上面代碼中,ES6用constructor方法,代替ES5的構(gòu)造函數(shù)。
(2)繼承
ES6的class結(jié)構(gòu)還允許使用extends關(guān)鍵字,表示繼承。
class MetaLanguage extends Language { constructor(x, y, z, version) { super(x, y, z); this.version = version; } summary() { //... super.summary(); } }
上面代碼的super方法,表示調(diào)用父類的構(gòu)造函數(shù)。
(1)基本用法
ES6允許定義模塊。也就是說,允許一個(gè)JavaScript腳本文件調(diào)用另一個(gè)腳本文件。
假設(shè)有一個(gè)circle.js,它是一個(gè)單獨(dú)模塊。
// circle.js export function area(radius) { return Math.PI * radius * radius; } export function circumference(radius) { return 2 * Math.PI * radius; }
然后,main.js引用這個(gè)模塊。
// main.js import { area, circumference } from 'circle'; console.log("圓面積:" + area(4)); console.log("圓周長(zhǎng):" + circumference(14));
另一種寫法是整體加載circle.js。
// main.jsmodule circle from 'circle'; console.log("圓面積:" + circle.area(4)); console.log("圓周長(zhǎng):" + circle.circumference(14));
(2)模塊的繼承
一個(gè)模塊也可以繼承另一個(gè)模塊。
// circleplus.js export * from 'circle'; export var e = 2.71828182846; export default function(x) { return Math.exp(x); }
加載上面的模塊。
// main.js module math from "circleplus"; import exp from "circleplus"; console.log(exp(math.pi);
(3)模塊的默認(rèn)方法
還可以為模塊定義默認(rèn)方法。
// circleplus.js export default function(x) { return Math.exp(x); }
2013年3月,ECMAScript 6的草案封閉,不再接受新功能了。新的功能將被加入ECMAScript 7。根據(jù)JavaScript創(chuàng)造者Brendan Eich的設(shè)想,ECMAScript 7將使得JavaScript更適于開發(fā)復(fù)雜的應(yīng)用程序和函數(shù)庫。
ECMAScript 7可能包括的功能有:
Object.observe:對(duì)象與網(wǎng)頁元素的雙向綁定,只要其中之一發(fā)生變化,就會(huì)自動(dòng)反映在另一者上。
Multi-Threading:多線程支持。目前,Intel和Mozilla有一個(gè)共同的研究項(xiàng)目RiverTrail,致力于讓JavaScript多線程運(yùn)行。預(yù)計(jì)這個(gè)項(xiàng)目的研究成果會(huì)被納入ECMAScript標(biāo)準(zhǔn)。
Traits:它將是“類”功能(class)的一個(gè)替代。通過它,不同的對(duì)象可以分享同樣的特性。
其他可能包括的功能還有:更精確的數(shù)值計(jì)算、改善的內(nèi)存回收、增強(qiáng)的跨站點(diǎn)安全、類型化的更貼近硬件的(Typed, Low-level)操作、國(guó)際化支持(Internationalization Support)、更多的數(shù)據(jù)結(jié)構(gòu)等等。
Sayanee Basu, Use ECMAScript 6 Today
Ariya Hidayat, Toward Modern Web Apps with ECMAScript 6
Nick Fitzgerald, Destructuring Assignment in ECMAScript 6
jmar777, What's the Big Deal with Generators?
Nicholas C. Zakas, Understanding ECMAScript 6 arrow functions
Dale Schouten, 10 Ecmascript-6 tricks you can perform right now
Mozilla Developer Network, Iterators and generators
Steven Sanderson, Experiments with Koa and JavaScript Generators
Matt Baker, Replacing callbacks with ES6 Generators
Domenic Denicola, ES6: The Awesome Parts
Casper Beyer, ECMAScript 6 Features and Tools
Luke Hoban, ES6 features