預計在2014年底,ECMAScript 6將會正式發布,他的草案在13年3月份被凍結,后續提出新特性將會移至ECMASript 7中。目前還沒有哪款瀏覽器實現了ES6的全部內容,兼容性最強的一款要數FireFox了。具體情況可以在這里查看。
關於 ECMAScript 6 草案,我在博客里頭復制了一份,可以點擊這里。
JavaScript的內容是越來越豐富,在ES6中還添加了模塊(module)和類(class),感覺他已經失去了曾經的單純了,不知道這些新功能的補充對開發者來說是福音還是負擔。之前寫過兩篇關於ES6的文章,ECMAScript 6 簡介 和 ECMAScript 6中的let和const關鍵詞,本文將一一介紹ES6中的一些新特性。
本文地址:http://www.cnblogs.com/hustskyking/p/ecmascript6-overview.html,轉載請注明源地址。
注意:如果想測試以下屬性,請安裝 0.11+ 版本的 node,並添加上 --harmony 參數。
一、let 和 const
這個內容在 ECMAScript 6中的let和const關鍵詞 一文中已經介紹過了。簡單來說就是一句話:ES6中引入了塊級作用域,let的有效區間是他所在的 {}
大括號中。const 為常量,定義之后不能更改,也刪除不了。
> const PI = 3.14;
> Object.getOwnPropertyDescriptor(window, PI) Object {value: 3.1415, writable: false, enumerable: true, configurable: false}
writable 和 configurable 都是 false。
二、多變量的模式賦值
寫過 coffee-script 的人都知道,我們可以這樣給一個數據賦值:
num[1..3] = ["hello", "i'am", "Barret Lee"]
ES6中也允許類似的多變量賦值:
var [x, y, z] = ["hello", "i'am", "Barret Lee"];
更強大的是,他還適合對象:
var { foo, bar } = { foo: "Barret", bar: "Lee" };
這種賦值方式是模式匹配的,只要左側跟右側對應,便可以成功賦值。感覺新手不會太適應這種寫法。
三、數組推導
先看例子:
var a1 = [1, 2, 3, 4]; var a2 = [i * 2 for (i of a1)]; a2 // [2, 4, 6, 8]
這東西只是簡化了編程,沒有從根本上增加功能和特性,寫 coffee 的人應該比較喜歡,我看着還是有點不習慣,其實上面的寫法就等價於:
var a1 = [1, 2, 3, 4]; var a2 = a1.map(function (i) { return i * 2 });
我想除非是代碼長度有限制,否則這玩意兒正式出來了我也不會用它。
四、字符串的擴展
這一塊的內容相當於是給 JS 編碼打一個補丁,這個補丁用來彌補雙字節 UTF-16 字符帶來的問題,引入的各個函數也只是對不同場景的修復。這個擴展還是相當重要的,尤其是 ArrayBuffer 中數據類型的相關處理,涉及到很多類似 Float64Array Uint32Array 等類型化數組的處理,我在 你所不知道的JavaScript數組 曾提到過。
1. codePointAt
這個地方需要解釋下 JavaScript 對字符的儲存模式,JavaScript 中的字符串是以 UTF-16 為代碼單元,通常我們使用的字符范圍都在 Unicode 值 0x10000 以內,他們對應的 UTF-16 就是它們自身,但 Unicode 中也存在這個范圍之外的字符,這時候就需要兩個 UTF-16 字符來描述,比如:
alert("𠐀".length); //2
因為字符串的 length 表示的並不是字符個數,而是 UTF-16 的單元個數。關於這方面知識的具體介紹,可以戳這里。
ES6提供了 codePointAt 函數來正確處理 4 個字節儲存的字符。
var s = "𠐀二"; s.codePointAt(0);
codePointAt 會把 s 中的兩個字符都正確解析出來,相當於智能的將儲存單元切換為 2 或者 4.
2. fromCodePoint
對應 String.fromCharCode 的能夠智能解析 s 的函數是 String.fromCodePoint
3. 字符的Unicode表示法
我們知道可以使用 \u0000-\uFFFF 來表示一個 Unicode 字符,依然是上面的問題,如果字符超出了這個范圍,比如 "\u10000" 這個字符,便不能正確的解析出來,ES6中可以這樣:
#\u{10000}
用大括號括起來就可以正常解析超過 FFFF 的字符了。
4. 正則修飾符 u
/^.$/.test("𠐀") // false /^..$/.test("𠐀") // true /^.$/u.test("𠐀") // true
看到上面三個測試,相信你已經知道是什么意思了。
5. 幾個沒啥意思的函數
contains(), startsWith(), endsWith(), repeat()
都是對 String 的拓展,顧名就能思意。
6. 模板字符串
之前寫過一篇關於 TEMPLATE 標簽的文章,這里我們又看到了一個跟模板相關的東西。
// 字符串中嵌入變量 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"
配合 TEMPLATE 標簽使用是比較方便的,不過 TEMPLATE 標簽可以很方便的使用自定義標簽,這個特性也不是很突出了。
<div id="test"> <x-from>DOM文檔</x-from> <x-name>test元素</x-name> </div> <template id="temp"> 我是來自 <strong><content select="x-from"></content></strong> 中的 <strong><content select="x-name"></content></strong> 的數據。 </template> <script> var root = test.webkitCreateShadowRoot(); root.appendChild(document.importNode(temp.content)); </script>
五、數值的擴展
1. 二進制和八進制表示法
0b111110111 === 503 // true 0o767 === 503 // true
前綴 0b 和 0o 表示二進制和八進制數值
2. 擴展函數
- Number.isFinite()
- Number.isNaN()
- Number.parseInt()
- Number.parseFloat()
- Number.isInteger()
具體細節可以去 MDN 上搜查。
六、對象的擴展
在 ES5.1 中我們看到了 Object 的靈活性提高了很多,加入了 create、defineProperty、freeze 等等很多十分有用的方法,而在 ES6 中,依然繼續加強:
1. Object.is()
比較嚴格相等,傳入兩個參數,其功能跟 ===
差不多,不同的是:+0不等於-0,NaN與本身恆等
+0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
2. proto屬性
obj.__proto__ = otherObj;
這東西瀏覽器早就實現了,只是在 ES6 才被納入標准中,還記得這東西被用來判斷是否為 IE 么:
var isIE = "__proto__" in {} ? false : true;
3. 增強的對象寫法
又是一個裝飾品:
var Person = { name: 'Barret Lee', //等同於birth: birth birth, // 等同於hello: function ()... hello() { console.log('My Name Is', this.name); } };
4. 允許變量滲入 key 中
var name = "my name"; var Me = { [name]: "Barret Lee", "name": "李靖" }; Me[name] // "Barret Lee" Me["my name"] // "Barret Lee" Me["name"] // "李靖"
挺方便的,不過容易出錯,比如上面,看暈了吧~
5. Symbol
一種從類型上區分數據的工具,他是一個原始類型的值,不是對象,很適合做標識符。
6. Proxy
Proxy 內置的一個代理工具,使用他可以在對象處理上加一層屏障:
var plain = { name : "Barret Lee" }; var proxy = new Proxy(plain, { get: function(target, property) { return property in target ? target[property] : "我擦"; } }); proxy.name // "Barret Lee" proxy.title // "我擦"
Proxy(target, handler), 這里的 handler 可以是 set get has hasOwn keys 等等方法,具體可以移步這里:http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies
七、函數的擴展
1. 參數設置默認值
function Point(x = 0, y = 0) { this.x = x; this.y = y; } var p = new Point(); // p = { x:0, y:0 }
他這個參數可以初始化相當於暴露更多的底層接口吧,提高了函數的拓展性。
2. rest運算符(...)
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(1, 2 ,3) // 6
這里的 values 需要用 val of values 來遍歷。
3. 箭頭函數(=>)
之前寫過 coffee ,所以對這個我還是比較熟悉的:
var f = (a, b) => {return a + b};
這個跟上面提到的一些內容一樣,都是裝飾性的,沒太多用途。
八、Set和Map數據結構
1. Set
簡單點解釋,他就是一個沒有重復數值的數組。或者說他就是一個數組的 hash 表。
var items = new Set([1,2,3,4,5,5,5,5]); for (i of s) {console.log(i)} // 2 3 4 5
其遍歷也是使用 val of values,他沒有繼承 Array 的方法,自己的幾個方法是:
- size():返回成員總數。
- add(value):添加某個值。
- delete(value):刪除某個值。
- has(value):返回一個布爾值,表示該值是否為set的成員。
- clear():清除所有成員。
轉化為數組的方式:
var items = new Set([1, 2, 3, 4, 5]); var array = Array.from(items);
2. Map
Map 是一個“超對象”,其 key 除了可以是 String 類型之外,還可以為其他類型(如:對象)
var m = new Map(); o = {p: "Hello World"}; m.set(o, "content") console.log(m.get(o)) // "content"
他的方法和 Set 差不多:
- size:返回成員總數。
- set(key, value):設置一個鍵值對。
- get(key):讀取一個鍵。
- has(key):返回一個布爾值,表示某個鍵是否在Map數據結構中。
- delete(key):刪除某個鍵。
- clear():清除所有成員。
- keys():返回鍵名的遍歷器。
- values():返回鍵值的遍歷器。
- entries():返回所有成員的遍歷器。
3. WeakMap
如果說Map 是一個“超對象”,那 WeakMap 就是個 “弱超對象”,他的鍵值只能是除 null 以外的對象。他的存在是為了方便垃圾回收。
八、遍歷器(Iterator)
相比 Array,他對數據的管理更為緊湊,而且可操縱性也比較強,以后會成為一個比較通用的 JS 工具。
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'
一個對象只要具備了next方法,就可以用for...of循環遍歷它的值。
for (var n of it) { if (n > 5) break; console.log(n); }
九、Generator 函數
Generator就是一個改裝了的 Iterator 遍歷器,通過 yield 來增加一個 next() 節點。
聲明一個 Generator 的方法:
function * foo( input ) { var res = yield input; }
使用方式:
function * foo( input ) { var res = yield input; } var g = foo(10); g.next(); // { value: 10, done: false } g.next(); // { value: undefined, done: true }
Genrator的標識就是函數名前面有個 *
號,由於每個 yield 都會增加一個 next() 節點,當我們在一個 Generator 中添加多個 yield 的時候:
function* G() { yield 'Barret'; yield 'Lee'; } var g = G(); g.next(); // "Barret" g.next(); // "Lee" g.next(); // undefined g.next(); // Error: Generator has already finished
相比 Iterator,我更喜歡 Generator。
十、Promise 對象
ES6將 Promise 納入規范之后,很多瀏覽器根據 Promise/A+ 規范實現了一套 API,Promise對象是對異步操作的平坦式表達,避免了 callback hell,也就是多層嵌套的回調函數。這方面的東西可以參考我之前寫的 JavaScript異步編程原理.
這里是 Promise/A+ 規范文檔,感興趣的可以閱讀一下。
在Promises/A規范中,每個任務都有三種狀態:默認(pending)、完成(fulfilled)、失敗(rejected)。
- 默認狀態可以單向轉移到完成狀態,這個過程叫resolve,對應的方法是deferred.resolve(promiseOrValue);
- 默認狀態還可以單向轉移到失敗狀態,這個過程叫reject,對應的方法是deferred.reject(reason);
- 默認狀態時,還可以通過deferred.notify(update)來宣告任務執行信息,如執行進度;
- 狀態的轉移是一次性的,一旦任務由初始的pending轉為其他狀態,就會進入到下一個任務的執行過程中。
十一、Class和Module
1. Class
Class,對象模板:
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '('+this.x+', '+this.y+')'; } }
一下子就有了 C++/Java 的感覺了,在類中可以使用 extends 繼承:
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // same as super.constructor(x, y) this.color = color; } toString() { return this.color+' '+super(); } }
2. Module
之前在這篇文章談過模塊化編程的必要性,時代在變化,需求也在變,ES6引入 Module 也算是與時俱進。ES6允許將獨立的js文件作為模塊,也就是說,允許一個JavaScript腳本文件調用另一個腳本文件,從而使得模塊化編程成為可能。
// circle.js export function area(radius) { return Math.PI * radius * radius; } export function circumference(radius) { return 2 * Math.PI * radius; }
如果我們要引入 circle.js 的函數,可以:
// main.js import { area, circumference } from 'circle'; console.log("圓面積:" + area(4)); console.log("圓周長:" + circumference(14));
如果要引入整個 circle 的內容:
// main.js module circle from 'circle'; console.log("圓面積:" + circle.area(4)); console.log("圓周長:" + circle.circumference(14));
模塊之間的繼承使用這條命令:
// other.js export * from 'circle';
這個東西,很好用,可惜了,純潔的 JS,被糟蹋成啥樣了=. =
十二、感謝
本文的書寫邏輯參考阮一峰的總結,並參入了一些個人主觀色彩,感謝前人栽樹!
十三、參考資料
- http://es6.ruanyifeng.com/ 阮一峰
- http://www.web-tinker.com/article/20558.html
http://www.web-tinker.com/article/20468.html
http://www.web-tinker.com/article/20520.html 次碳酸鈷 - http://huangj.in/765 H-Jin