ES6

我相信,在ECMAScript.next到來的時候,咱們如今天天都在寫的JavaScript代碼將會發生巨大的變化.接下來的一年將會是令JavaScript開發者們興奮的一年,愈來愈多的特性提案將被最終敲定,新一版本的JavaScript將會慢慢獲得普及.javascript

本文中,我將會講幾個我我的很期待的,但願能在2013年或者更晚一點使用上的新特性.php

ES.next目前的實現狀況

能夠經過查看Juriy Zaytsev總結的ECMAScript 6兼容性表格,和Mozilla的ES6實現狀況頁面以及經過使用現代瀏覽器的最新版本(好比Chrome CanaryFirefox Aurora),來了解目前有哪些已經實現了的ES.next特性.html

在Canary中,記得要進入chrome:flags打開'啓用實驗性JavaScript'選項以激活全部最新的的JavaScript特性.java

另外,許多ES.next特性還能夠經過使用Google的Traceur轉換編譯器(這裏有一些單元測試的例子)來體驗,以及一些shim項目好比ES6-ShimHarmony Collections,也實現了很多新特性.node

在Node.js(V8)中使用--harmony命令行選項能夠開啓一些試驗性質的ES.next特性,包括塊級做用域,WeakMap等等.git

模塊

咱們已經習慣了將咱們的代碼分割成爲更加便於管理的功能塊.在ES.next中,一個模塊(module)是就是一個module聲明,以及包含在該聲明中的一組代碼.模塊能夠用內聯方式(inline)聲明,也能夠引入一個外部的模塊文件.一個名爲Car的內聯模塊的寫法大體以下:程序員

  1. module Car {  
  2.   // 導入 …  
  3.   // 導出 …  
  4. }  

一個模塊實例就是一個被求過值的模塊,它已經被連接到了其餘的模塊身上或者已經有了詞法上的封裝數據.下面是一個模塊實例的例子:es6

  1. module myCar at "car.js";  

module聲明可使用在以下上下文中:github

  1. module UniverseTest {};  
  2. module Universe { module MilkyWay {} };  
  3. module MilkyWay = 'Universe/MilkyWay';  
  4. module SolarSystem = Universe.MilkyWay.SolarSystem;  
  5. module MySystem = SolarSystem;  

一個export聲明聲明瞭一個能夠被其餘模塊看到的局部函數或變量.web

  1. module Car {  
  2.   // 內部變量
  3.   var licensePlateNo = '556-343';  
  4.   // 暴露到外部的變量和函數
  5.   export function drive(speed, direction) {  
  6.     console.log('details:', speed, direction);  
  7.   }  
  8.   export module engine{  
  9.     export function check() { }  
  10.   }  
  11.   export var miles = 5000;  
  12.   export var color = 'silver';  
  13. };  

一個模塊可使用import導入任何它所須要的其餘模塊.導入模塊會讀取被導入模塊的全部可導出數據(好比上面的drive()miles等),但不能修改它們.導出的變量或函數能夠被重命名.

再次用到上面導出相關的例子,咱們如今能夠有選擇性的導入一些模塊中的功能.

好比咱們能夠導入drive():

  1. import drive from Car;  

還能夠能夠同時導入drive()miles:

  1. import {drive, miles} from Car;  

下面,咱們要講一下模塊加載器API的概念.模塊加載器可以讓咱們動態的加載所須要的腳本.相似於import, 咱們可使用被導入模塊中的全部用export聲明過的東西.

  1. // Signature: load(moduleURL, callback, errorCallback)  
  2. Loader.load('car.js', function(car) {  
  3.   console.log(car.drive(500, 'north'));  
  4. }, function(err) {  
  5.   console.log('Error:' + err);  
  6. });  

load()接受三個參數:

  • moduleURL: 表示一個模塊URL的字符串 (好比 "car.js")
  • callback: 一個回調函數,接受模塊加載,編譯,以及執行後的輸出結果
  • errorCallback: 一個回調函數,在加載或編譯期間發生錯誤時調用

關於類(class)

我不打算在本文中過多的講ES.next中的,若是你想知道類和模塊將會有什麼聯繫,Alex Russell曾經寫過一個很好的例子來講明這件事.

JavaScript中有了類,並不意味着要把JavaScript變成Java.ES.next中的類只是咱們已經熟悉的語義(好比函數,原型)的另一種聲明方式

下面是用來定義一個widget的ES.next代碼:

  1. module widgets {  
  2.   // ...  
  3.   class DropDownButton extends Widget {  
  4.     constructor(attributes) {  
  5.       super(attributes);  
  6.       this.buildUI();  
  7.     }  
  8.     buildUI() {  
  9.       this.domNode.onclick = function(){  
  10.         // ...  
  11.       };  
  12.     }  
  13.   }  
  14. }  

下面是去糖(de-sugared)後的作法,也就是咱們目前正在使用的方法:

  1. var widgets = (function(global) {  
  2.   // ...  
  3.   function DropDownButton(attributes) {  
  4.     Widget.call(this, attributes);  
  5.     this.buildUI();  
  6.   }  
  7.   DropDownButton.prototype = Object.create(Widget.prototype, {  
  8.     constructor: { value: DropDownButton },  
  9.     buildUI:     {  
  10.       value: function(e) {  
  11.         this.domNode.onclick = function(e) {  
  12.           // ...  
  13.         }  
  14.       }  
  15.     }  
  16.   });  
  17. })(this);  

ES.next的寫法的確讓代碼變的更可讀.這裏的class也就至關因而function,至少是作了目前咱們用function來作的一件事.若是你已經習慣而且也喜歡用JavaScript中的函數和原型,這種將來的語法糖也就不用在乎了.

這些模塊如何和AMD配合使用?

ES.next中的模塊是朝着正確的方向走了一步嗎?也許是吧.我本身的見解是:看相關的規範文檔是一碼事,實際上使用起來又是另外一碼事.在Harmonizr,Require HMTraceur中能夠體驗新的模塊語法,你會很是容易的熟悉這些語法,該語法可能會以爲有點像Python的感受(好比import語句).

我認爲,若是一些功能有足夠普遍的使用需求(好比模塊),那麼平臺(也就是瀏覽器)就應該原生支持它們.並且,並非只有我一我的這麼以爲. James Burke,發明了AMD和RequireJS的人,也曾經說過:

我想,AMD和RequireJS應該被淘汰了.它們的確解決了一個實際存在的問題,但更理想的狀況是,語言和運行環境應該內置相似的功能.模塊的原生支持應該可以覆蓋RequireJS 80%的使用需求,從這一點上說,咱們再也不須要使用任何用戶態(userland)的模塊加載庫了,至少在瀏覽器中是這樣.

不過James的質疑是ES.next的模塊是不是一個足夠好的解決方案,他曾在六月份談到過本身關於ES.next中模塊的一些想法 ES6 Modules: Suggestions for improvement以及再後來的一篇文章Why not AMD?.

Isaac Schlueter前段時間也寫過一些本身的想法,講到了ES6的模塊有哪些不足.嘗試一下下面這些選項,看看你的想法如何.

兼容目前引擎的Module實現

Object.observe()

經過Object.observe,咱們能夠觀察指定的對象,而且在該對象被修改時獲得通知.這種修改操做包括屬性的添加,更新,刪除以及從新配置.

屬性觀察是咱們常常會在MVC框架中看到的行爲,它是數據綁定的一個重要組件,AngularJS和Ember.js都有本身的解決方案.

這是一個很是重要的新功能,它不只比目前全部框架的同類實現性能要好,並且還能更容易的觀察純原生對象.

  1. // 一個簡單的對象能夠做爲一個模塊來使用  
  2. var todoModel = {  
  3.     label: 'Default',  
  4.     completed: false  
  5. };  
  6. // 咱們觀察這個對象 
  7. Object.observe(todoModel, function(changes) {  
  8.     changes.forEach(function(change, i) {  
  9.         console.log(change);  
  10.         /* 
  11.             哪一個屬性被改變了? change.name 
  12.             改變類型是什麼? change.type 
  13.             新的屬性值是什麼? change.object[change.name] 
  14.         */  
  15.     });  
  16. });  
  17. // 使用時:
  18. todoModel.label = 'Buy some more milk';  
  19. /* 
  20.     label屬性被改變了 
  21.     改變類型是屬性值更新 
  22.     當前屬性值爲'Buy some more milk' 
  23. */  
  24. todoModel.completeBy = '01/01/2013';  
  25. /* 
  26.     completeBy屬性被改變了 
  27.     改變類型是屬性被添加 
  28.     當前屬性值爲'01/01/2013' 
  29. */  
  30. delete todoModel.completed;  
  31. /* 
  32.     completed屬性被改變了 
  33.     改變類型是屬性被刪除 
  34.     當前屬性值爲undefined 
  35. */  

Object.observe立刻將會在Chrome Canary中實現(須要開啓"啓用實驗性JavaScript"選項).

兼容目前引擎的Object.observe()實現

Rick Waldron的這篇文章有關於Object.observe更詳細的介紹.

譯者注:Firefox很早就有了一個相似的東西:Object.prototype.watch.

默認參數值

默認參數值(Default parameter values)的做用是:在一些形參沒有被顯式傳值的狀況下,使用默認的初始化值來進行初始化.這就意味着咱們再也不須要寫相似options = options || {};這樣的語句了.

該語法形式就是把一個初始值賦值給對應的形參名:

  1. function addTodo(caption = 'Do something') {  
  2.     console.log(caption);  
  3. }  
  4. addTodo(); // Do something  

擁有默認參數值的形參只能放在形參列表的最右邊:

  1. function addTodo(caption, order = 4) {}  
  2. function addTodo(caption = 'Do something', order = 4) {}  
  3. function addTodo(caption, order = 10, other = this) {}  

已經實現該特性的瀏覽器: Firefox 18+

譯者注:Firefox 15就已經實現了默認參數值,做者所說的18只是說18支持,並非說18是第一個支持的版本.包括本文下面將要提到的chrome 24+等等,都有這個問題.

塊級做用域

塊級做用域引入了兩種新的聲明形式,能夠用它們定義一個只存在於某個語句塊中的變量或常量.這兩種新的聲明關鍵字爲:

  • let: 語法上很是相似於var, 但定義的變量只存在於當前的語句塊中
  • const: 和let相似,但聲明的是一個只讀的常量

使用let代替var能夠更容易的定義一個只在某個語句塊中存在的局部變量,而不用擔憂它和函數體中其餘部分的同名變量有衝突.在let語句內部用var聲明的變量和在let語句外部用var聲明的變量沒什麼差異,它們都擁有函數做用域,而不是塊級做用域.

譯者注:以防讀者看不懂,我用一個例子解釋一下上面的這句話,是這樣的:

複製代碼
let(var1 = 1) {
alert(var1); //彈出1,var1是個塊級做用域變量 var var2 = 2;
}
var var3 = 3;
alert(var2); //彈出2,雖然var2是在let語句內部聲明的,但它仍然是個函數做用域內的變量,由於使用的是var聲明 alert(var3); //彈出3 alert(var1); //拋出異常
複製代碼
  1. var x = 8;  
  2. var y = 0;  
  3. let (x = x+10, y = 12) {  
  4.   console.log(x+y); // 30  
  5. }  
  6. console.log(x + y); // 8  

實現let的瀏覽器: Firefox 18+, Chrome 24+

實現const的瀏覽器: Firefox 18+, Chrome 24+,  Safari 6+, WebKit, Opera 12+

譯者注:Firefox好久之前就支持了letconst,但這兩個舊的實現都是依據了當年的ES4草案.和目前的ES6草案有些區別,好比ES4中用const聲明的常量並無塊級做用域(和var同樣,只是值不可變),let也有一些細微差異,就不說了.因爲不多人使用舊版的Firefox(但個人主瀏覽器是FF3.6!),即便將來ES6和ES4中的一些東西有衝突,咱們基本也能夠忽略.

Map

我想大部分讀者已經熟悉了映射的概念,由於咱們過去一直都是用純對象來實現映射的.Map容許咱們將一個值映射到一個惟一的鍵上,而後咱們就能夠經過這個鍵獲取到對應的值,而不須要擔憂用普通對象實現映射時因原型繼承而帶來的問題.

使用set()方法,能夠在map中添加一個新的鍵值對,使用get()方法,能夠獲取到所存儲的值.Map對象還有其餘三個方法:

  • has(key) : 一個布爾值,代表某個鍵是否存在於map中
  • delete(key) : 刪除掉map中指定的鍵
  • size() : 返回map中鍵值對的個數
  1. let m = new Map();  
  2. m.set('todo', 'todo'.length);  // "todo" → 4  
  3. m.get('todo');                 // 4  
  4. m.has('todo');                 // true  
  5. m.delete('todo');              // true  
  6. m.has('todo');                 // false  

已經實現Map的瀏覽器: Firefox 18+

Nicholas Zakas的這篇文章有關於Map更詳細的介紹.

兼容目前引擎的Map實現

譯者注:我翻譯過尼古拉斯的這篇文章:[譯]ECMAScript 6中的集合類型,第二部分:Map.

做者可能不知道,10月份的ES6草案中,Map.prototype.sizeSet.prototype.size都從size()方法改爲size訪問器屬性了.同時Map對象新添加的方法還有不少,clear()用來清空一個map,forEach()用來遍歷一個map,還有items(),keys(),values()等.Set對象也相似,有很多做者沒提到的方法,下面的Set小節我就不指出了.

另外,在ES5中,在把對象當成映射來使用的時候,爲了防止原型繼承帶來的問題(好比在twitter中,@__proto__能讓瀏覽器卡死),能夠用var hash = Object.create(null)代替var hash = {};

Set

正如Nicholas Zakas在他的文章中所說,對於那些接觸過Ruby和Python等其餘語言的程序員來講,Set並非什麼新東西,但它的確是在JavaScript中一直都缺乏的特性.

任何類型的數據均可以存儲在一個set中,但每一個值只能存儲一次(不能重複).利用Set能夠很方便的建立一個不包含任何重複值的有序列表.

  • add(value) – 向set中添加一個值.
  • delete(value) – 從set中刪除value這個值.
  • has(value) – 返回一個布爾值,代表value這個值是否存在於這個set中.
  1. let s = new Set([1, 2, 3]);  // s有1, 2, 3三個元素.  
  2. s.has(-Infinity);            // false  
  3. s.add(-Infinity);            // s有1, 2, 3, -Infinity四個元素.  
  4. s.has(-Infinity);            // true  
  5. s.delete(-Infinity);         // true  
  6. s.has(-Infinity);            // false  

Set對象的一個做用是用來下降過濾操做(filter方法)的複雜度.好比:

  1. function unique(array) {  
  2.     var seen = new Set;  
  3.     return array.filter(function (item) {  
  4.         if (!seen.has(item)) {  
  5.             seen.add(item);  
  6.             return true;  
  7.         }  
  8.     });  
  9. }  

這個利用Set來進行數組去重的函數的複雜度爲O(n).而其餘現有數組去重方法的複雜度幾乎都爲O(n^2).

已經實現Set的瀏覽器: Firefox 18, Chrome 24+.

Nicholas Zakas的這篇文章有關於Set更詳細的介紹.

兼容目前引擎的Set實現

譯者注:我翻譯過尼古拉斯的這篇文章:[譯]ECMAScript 6中的集合類型,第一部分:Set.

若是讓我來實現一個ES6下的數組去重函數的話,我會這麼寫:

function unique(array) {   
return [v for(v of Set(array))]
}

該函數使用到了ES6中的for-of遍歷,以及數組推導式.不過效率比上面使用filter去重的方法稍微差點.Firefox最新版中已經能夠執行這個函數.

另外,藉助於下面將會提到的Array.from方法,還有更簡單高效的寫法:

>Array.from(new Set([ 1, 1, 2, 2, 3, 4 ]));
[1,2,3,4]

甚至,藉助於ES6中的展開(spread)操做,還有可能這樣實現:

>[ ... new Set([ 1, 1, 2, 2, 3, 4 ]) ];
[1,2,3,4]

WeakMap

WeakMap的鍵只能是個對象值,並且該鍵持有了所引用對象的弱引用,以防止內存泄漏的問題.這就意味着,若是一個對象除了WeakMap的鍵之外沒有任何其餘的引用存在的話,垃圾回收器就會銷燬這個對象.

WeakMap的另一個特色是咱們不能遍歷它的鍵,而Map能夠.

  1. let m = new WeakMap();  
  2. m.set('todo', 'todo'.length);  // 異常,鍵必須是個對象值!  
  3. // TypeError: Invalid value used as weak map key  
  4. m.has('todo');                 // 一樣異常!  
  5. // TypeError: Invalid value used as weak map key  
  6. let wmk = {};  
  7. m.set(wmk, 'thinger');  // wmk → 'thinger'  
  8. m.get(wmk);             // 'thinger'  
  9. m.has(wmk);             // true  
  10. m.delete(wmk);          // true  
  11. m.has(wmk);             // false  

已經實現WeakMap的瀏覽器: Firefox 18+, Chrome 24+.

兼容目前引擎的WeakMap實現

Nicholas Zakas的這篇文章有關於WeakMap更詳細的介紹.

譯者注:我翻譯過尼古拉斯的這篇文章:[譯]ECMAScript 6中的集合類型,第三部分:WeakMap

代理

代理(Proxy)API容許你建立一個屬性值在運行期間動態計算的對象.還能夠利用代理API"鉤入"其餘的對象,實現例如打印記錄和賦值審覈的功能.

  1. var obj = {foo: "bar"};  
  2. var proxyObj = Proxy.create({  
  3.   get: function(obj, propertyName) {  
  4.     return 'Hey, '+ propertyName;  
  5.   }  
  6. });  
  7. console.log(proxyObj.Alex); // "Hey, Alex"  

能夠看看Nicholas Zakas的這篇文章這個例子.

實現代理API的瀏覽器: Firefox 18+, Chrome 24+

譯者注:做者不知道的是,一共有過兩個代理API的提案,一個是舊的Catch-all Proxies,一個是新的直接代理(Direct Proxies).前者已被廢棄.二者的區別在這裏.V8(Chrome和Node.js)實現的是前者,Firefox18及以後版本實現的是後者(17及以前版本實現的是前者).尼古拉斯在2011年寫的文章也應該是過期的.

一些新的API

Object.is

Object.is是一個用來比較兩個值是否相等的函數.該函數和===最主要的區別是在對待特殊值NaN與自身以及正零與負零之間的比較上.Object.is的判斷結果是:NaN與另一個NaN是相等的,以及+0和-0是不等的.

  1. Object.is(0, -0); // false  
  2. Object.is(NaN, NaN); // true  
  3. 0 === -0; // true  
  4. NaN === NaN; // false  

實現了Object.is的瀏覽器: Chrome 24+

兼容目前引擎的Object.is實現

譯者注:Object.is方法和嚴格相等===運算符的區別體如今ES標準內部就是SameValue算法嚴格相等比較算法的區別.

譯者注:若是我沒有理解錯這條BE的推特的話,Object.is要更名成爲Object.sameValue了.

Array.from

Array.from: 將參數中的類數組(array-like)對象(好比arguments, NodeList, DOMTokenList (classList屬性就是這個類型), NamedNodeMap (attributes屬性就是這個類型))轉換成數組並返回,好比轉換一個純對象:

  1. Array.from({  
  2.     0: 'Buy some milk',  
  3.     1: 'Go running',  
  4.     2: 'Pick up birthday gifts',  
  5.     length: 3  
  6. });  

再好比轉換一個DOM節點集合:

  1. var divs = document.querySelectorAll('div');  
  2. Array.from(divs);  
  3. // [<div class="some classes" data-info="12"></div>, <div data-info="10"></div>]  
  4. Array.from(divs).forEach(function(node) {  
  5.     console.log(node);  
  6. });  

兼容目前引擎的Array.from實現

譯者注:從做者舉的兩個例子能夠看出,Array.from基本至關於目前使用的[].prototype.slice.call.

目前的草案也的確是這樣規定的,但從Rick Waldron(TC39成員)在原文評論中給出的代碼能夠看出,也許Array.from將來也能將Set對象(非類數組對象,但可迭代)轉換成數組.

譯者注:除了這兩個API,還有不少個新添加的API,好比

Number.isFiniteisNaNisIntegertoInteger

String.prototype.repeatstartsWith, endsWith, contains, toArray

下面給出兩個頗有用的連接:

Mozilla正計劃實現的ES6特性https://wiki.mozilla.org/ES6_plans.

ES6目前的全部特性提案http://wiki.ecmascript.org/doku.php?id=harmony:proposals

總結

ES.next中添加了許多被認爲是JavaScript中缺失已久的新特性.雖然ES6規範計劃在2013年年末發佈,不過瀏覽器們已經開始實現其中的一些特性了,這些特性被普遍使用也只是時間問題了.

在ES6徹底實現以前,咱們可使用一些轉換編譯器(transpiler)或者shim來體驗其中一些特性.

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息