本文基于lukehoban/es6features ,同時參考了大量博客資料,具體見文末援用。
ES6(ECMAScript 6)是行將到來的新版本JavaScript語言的標準,代號harmony(和諧之意,明顯沒有跟上我國的步伐,我們已進入中國夢版本了)。上1次標準的制定還是2009年出臺的ES5。目前ES6的標準化工作正在進行中,預計會在14年12月份放出正式敲定的版本。但大部份標準已就緒,且各閱讀器對ES6的支持也正在實現中。要查看ES6的支持情況請點此。
目前想要運行ES6代碼的話,可以用google/traceur-compiler將代碼轉譯。點此訪問traceur-compiler 在線版本時實編輯ES6代碼并查看轉換后的結果,代碼運行結果會在console顯示。
另外,關于Google Traceur,業界大神Addy Osmani利用前者寫了個Chrome插件ES6 Tepl,安裝后也能夠進行ES6的測試。
固然,其實不是所有ES6新特性都被實現了,所以上面的方法可以測試大部份,有1些還是沒法測試的。
雖然ES6都還沒真正發布,但已有用ES6重寫的程序了,各種關于ES789的提議已開始了,這你敢信。潮流不是我等大眾所能追逐的。
潮流雖然太快,但我們不停下學習的步伐,就不會被潮流丟下的,下面來領略下ES6中新特性,1堵新生代JS的風采。
如果你會C#或Java,你肯定知道lambda表達式,ES6中新增的箭頭操作符=>便有異曲同工之妙。它簡化了函數的書寫。操作符左側為輸入的參數,而右側則是進行的操作和返回的值Inputs=>outputs。
我們知道在JS中回調是常常的事,而1般回調又以匿名函數的情勢出現,每次都需要寫1個function,甚是繁瑣。當引入箭頭操作符后可以方便地寫回調了。請看下面的例子。
var array = [1, 2, 3];
//傳統寫法
array.forEach(function(v, i, a) {
console.log(v);
});
//ES6
array.forEach(v = > console.log(v));
大家可以打開文章開頭提到的traceur在線代碼轉譯頁面輸入代碼來查看效果。
ES6中添加了對類的支持,引入了class關鍵字(其實class在JavaScript中1直是保存字,目的就是斟酌到可能在以后的新版本中會用到,現在終究派上用處了)。JS本身就是面向對象的,ES6中提供的類實際上只是JS原型模式的包裝。現在提供原生的class支持后,對象的創建,繼承更加直觀了,并且父類方法的調用,實例化,靜態方法和構造函數等概念都更加形象化。
下面代碼展現了類在ES6中的使用。再次啰嗦1句,你可以將代碼貼到traceur自己查看運行結果。
//類的定義
class Animal {
//ES6中新型構造器
constructor(name) {
this.name = name;
}
//實例方法
sayName() {
console.log('My name is '+this.name);
}
}
//類的繼承
class Programmer extends Animal {
constructor(name) {
//直接調用父類構造器進行初始化
super(name);
}
program() {
console.log("I'm coding...");
}
}
//測試我們的類
var animal=new Animal('dummy'),
wayou=new Programmer('wayou');
animal.sayName();//輸出 ‘My name is dummy’
wayou.sayName();//輸出 ‘My name is wayou’
wayou.program();//輸出 ‘I'm coding...’
對象字面量被增強了,寫法更加簡潔與靈活,同時在定義對象的時候能夠做的事情更多了。具體表現在:
這樣1來,對象字面量與前面提到的類概念更加吻合,在編寫面向對象的JavaScript時更加輕松方便了。
//通過對象字面量創建對象
var human = {
breathe() {
console.log('breathing...');
}
};
var worker = {
__proto__: human, //設置此對象的原型為human,相當于繼承human
company: 'freelancer',
work() {
console.log('working...');
}
};
human.breathe();//輸出 ‘breathing...’
//調用繼承來的breathe方法
worker.breathe();//輸出 ‘breathing...’
字符串模板相對簡單易懂些。ES6中允許使用反引號 ` 來創建字符串,此種方法創建的字符串里面可以包括由美元符號加花括號包裹的變量${vraible}。如果你使用過像C#等后端強類型語言的話,對此功能應當不會陌生。
//產生1個隨機數
var num=Math.random();
//將這個數字輸出到console
console.log(`your num is ${num}`);
自動解析數組或對象中的值。比如若1個函數要返回多個值,常規的做法是返回1個對象,將每一個值做為這個對象的屬性返回。但在ES6中,利用解構這1特性,可以直接返回1個數組,然后數組中的值會自動被解析到對應接收該值的變量中。
var [x,y]=getVal(),//函數返回值的解構
[name,,age]=['wayou','male','secrect'];//數組解構
function getVal() {
return [ 1, 2 ];
}
console.log('x:'+x+', y:'+y);//輸出:x:1, y:2
console.log('name:'+name+', age:'+age);//輸出: name:wayou, age:secrect
現在可以在定義函數的時候指定參數的默許值了,而不用像之前那樣通過邏輯或操作符來到達目的了。
function sayHello(name){
//傳統的指定默許參數的方式
var name=name||'dude';
console.log('Hello '+name);
}
//應用ES6的默許參數
function sayHello2(name='dude'){
console.log(`Hello ${name}`);
}
sayHello();//輸出:Hello dude
sayHello('Wayou');//輸出:Hello Wayou
sayHello2();//輸出:Hello dude
sayHello2('Wayou');//輸出:Hello Wayou
不定參數是在函數中使用命名參數同時接收不定數量的未命名參數。這只是1種語法糖,在之前的JavaScript代碼中我們可以通過arguments變量來到達這1目的。不定參數的格式是3個句點后跟代表所有不定參數的變量名。比以下面這個例子中,…x代表了所有傳入add函數的參數。
//將所有參數相加的函數
function add(...x){
return x.reduce((m,n)=>m+n);
}
//傳遞任意個數的參數
console.log(add(1,2,3));//輸出:6
console.log(add(1,2,3,4,5));//輸出:15
拓展參數則是另外一種情勢的語法糖,它允許傳遞數組或類數組直接做為函數的參數而不用通過apply。
var people=['Wayou','John','Sherlock'];
//sayHello函數本來接收3個單獨的參數人妖,人2和人3
function sayHello(people1,people2,people3){
console.log(`Hello ${people1},${people2},${people3}`);
}
//但是我們將1個數組以拓展參數的情勢傳遞,它能很好地映照到每一個單獨的參數
sayHello(...people);//輸出:Hello Wayou,John,Sherlock
//而在之前,如果需要傳遞數組當參數,我們需要使用函數的apply方法
sayHello.apply(null,people);//輸出:Hello Wayou,John,Sherlock
可以把let看成var,只是它定義的變量被限定在了特定范圍內才能使用,而離開這個范圍則無效。const則很直觀,用來定義常量,即沒法被更改值的變量。
for (let i=0;i<2;i++)console.log(i);//輸出: 0,1
console.log(i);//輸出:undefined,嚴格模式下會報錯
我們都知道for in 循環用于遍歷數組,類數組或對象,ES6中新引入的for of循環功能類似,不同的是每次循環它提供的不是序號而是值。
var someArray = [ "a", "b", "c" ];
for (v of someArray) {
console.log(v);//輸出 a,b,c
}
注意,此功能google traceur并未實現,所以沒法摹擬調試,下面有些功能也是如此
這1部份的內容有點生澀,詳情可以參見這里。以下是些基本概念。
在ES6標準中,JavaScript原生支持module了。這類將JS代碼分割成不同功能的小塊進行模塊化的概念是在1些3方規范中流行起來的,比如CommonJS和AMD模式。
將不同功能的代碼分別寫在不同文件中,各模塊只需導出公共接口部份,然后通過模塊的導入的方式可以在其他地方使用。下面的例子來自tutsplus:
// point.js
module "point" {
export class Point {
constructor (x, y) {
public x = x;
public y = y;
}
}
}
// myapp.js
//聲明援用的模塊
module point from "/point.js";
//這里可以看出,雖然聲明了援用的模塊,還是可以通過指定需要的部份進行導入
import Point from "point";
var origin = new Point(0, 0);
console.log(origin);
這些是新加的集合類型,提供了更加方便的獲得屬性值的方法,不用像之前1樣用hasOwnProperty來檢查某個屬性是屬于原型鏈上的呢還是當前對象的。同時,在進行屬性值添加與獲得時有專門的get,set 方法。
下方代碼來自es6feature
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
有時候我們會把對象作為1個對象的鍵用來寄存屬性值,普通集合類型比如簡單對象會禁止垃圾回收器對這些作為屬性鍵存在的對象的回收,有造成內存泄漏的危險。而WeakMap,WeakSet則更加安全些,這些作為屬性鍵的對象如果沒有別的變量在援用它們,則會被回收釋放掉,具體還看下面的例子。
正文代碼來自es6feature
// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });//由于添加到ws的這個臨時對象沒有其他變量援用它,所以ws不會保存它的值,也就是說這次添加其實沒成心思
Proxy可以監聽對象身上產生了甚么事情,并在這些事情產生后履行1些相應的操作。1下子讓我們對1個對象有了很強的追蹤能力,同時在數據綁定方面也很有用途。
以下例子借用自這里。
//定義被偵聽的目標對象
var engineer = { name: 'Joe Sixpack', salary: 50 };
//定義處理程序
var interceptor = {
set: function (receiver, property, value) {
console.log(property, 'is changed to', value);
receiver[property] = value;
}
};
//創建代理以進行偵聽
engineer = Proxy(engineer, interceptor);
//做1些改動來觸發代理
engineer.salary = 60;//控制臺輸出:salary is changed to 60
上面代碼我已加了注釋,這里進1步解釋。對處理程序,是在被偵聽的對象身上產生了相應事件以后,處理程序里面的方法就會被調用,上面例子中我們設置了set的處理函數,表明,如果我們偵聽的對象的屬性被更改,也就是被set了,那這個處理程序就會被調用,同時通過參數能夠得知是哪一個屬性被更改,更改成了甚么值。
我們知道對象實際上是鍵值對的集合,而鍵通常來講是字符串。而現在除字符串外,我們還可以用symbol這類值來做為對象的鍵。Symbol是1種基本類型,像數字,字符串還有布爾1樣,它不是1個對象。Symbol 通過調用symbol函數產生,它接收1個可選的名字參數,該函數返回的symbol是唯1的。以后就能夠用這個返回值做為對象的鍵了。Symbol還可以用來創建私有屬性,外部沒法直接訪問由symbol做為鍵的屬性值。
以下例子來自es6features
(function() {
// 創建symbol
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
... this[key] ...
}
};
})();
var c = new MyClass("hello")
c["key"] === undefined//沒法訪問該屬性,由于是私有的
對Math,Number,String還有Object等添加了許多新的API。下面代碼一樣來自es6features,對這些新API進行了簡單展現。
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) })
Promises是處理異步操作的1種模式,之前在很多3方庫中有實現,比如jQuery的deferred 對象。當你發起1個異步要求,并綁定了.when(), .done()等事件處理程序時,其實就是在利用promise模式。
//創建promise
var promise = new Promise(function(resolve, reject) {
// 進行1些異步或耗時操作
if ( /*如果成功 */ ) {
resolve("Stuff worked!");
} else {
reject(Error("It broke"));
}
});
//綁定處理程序
promise.then(function(result) {
//promise成功的話會履行這里
console.log(result); // "Stuff worked!"
}, function(err) {
//promise失敗會履行這里
console.log(err); // Error: "It broke"
});
總結就是1句話,前后端差異愈來愈小了。
學習資料:
es6features : 經典的ES6新特性預覽,github
逼近
10k start
.
ES6新特性概覽:一樣也很全面的特性介紹的中文版。
閱讀器兼容性列表:可以看到還是全線飄紅的~~~
V8 和 Node 支持特新列表: 據此在V8和node上使用這些新特新 .
待補充 ......
ECMAScript? 2015 Language Specification : 最重要的ES6產品規格書,甚么教程也脫離不了這里的標準, 英文好的還是多看點。
ES6 In Depth Articles :Mozilar hack社區出品, 必屬精品我就不知道了,深入學習ES6系列, 每篇文章都介紹的很詳細,值得1看。
ECMAScript 6 入門 :英文看不懂沒關系, 我們的阮老師最喜歡幫我們干這些活(很敬佩的大好人),不過翻譯難免也有些錯漏,所以看完1章要看下評論,有毛病會有人指出 !
ECMAScript 6 入門:
待補充 ......
由于目前各種環境下對ES6的支持程度其實不高,在閱讀器運行ES6代碼是不太現實的,不過我們1樣可以用ES6的新特新寫代碼,只是最后運行之前需要將其編譯為 ES5的代碼, 所以自然少不了編譯工具,這里只介紹1個,也是最好用的1個: babel .
github
官網
在線編譯系統
gulp-babel
grunt-babel
待補充 ......