ES6(ECMAScript 6)是即将到來的新版本JavaScript語言的标準,代号harmony(和諧之意,顯然沒有跟上我國的步伐,我們已經進入中國夢版本了)。上一次标準的制訂還是2009年出台的ES5。目前ES6的标準化工作正在進行中,預計會在14年12月份放出正式敲定的版本。但大部分标準已經就緒,且各浏覽器對ES6的支援也正在實作中。要檢視ES6的支援情況請點此。
目前想要運作ES6代碼的話,可以用google/traceur-compiler将代碼轉譯。點此通路traceur-compiler 線上版本時實編輯ES6代碼并檢視轉換後的結果,代碼運作結果會在console顯示。
另外,關于Google Traceur,業界大神Addy Osmani利用前者寫了個Chrome插件ES6 Tepl,安裝後也可以進行ES6的測試。
當然,并不是所有ES6新特性都被實作了,是以上面的方法可以測試大部分,有一些還是無法測試的。
雖然ES6都還沒真正釋出,但已經有用ES6重寫的程式了,各種關于ES789的提議已經開始了,這你敢信。潮流不是我等大衆所能追趕的。
潮流雖然太快,但我們不停下學習的步伐,就不會被潮流丢下的,下面來領略下ES6中新特性,一堵新生代JS的風采。
箭頭操作符
如果你會C#或者Java,你肯定知道lambda表達式,ES6中新增的箭頭操作符=>便有異曲同工之妙。它簡化了函數的書寫。操作符左邊為輸入的參數,而右邊則是進行的操作以及傳回的值Inputs=>outputs。
我們知道在JS中回調是經常的事,而一般回調又以匿名函數的形式出現,每次都需要寫一個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中一直是保留字,目的就是考慮到可能在以後的新版本中會用到,現在終于派上用場了)。JS本身就是面向對象的,ES6中提供的類實際上隻是JS原型模式的包裝。現在提供原生的class支援後,對象的建立,繼承更加直覺了,并且父類方法的調用,執行個體化,靜态方法和構造函數等概念都更加形象化。
下面代碼展示了類在ES6中的使用。再次啰嗦一句,你可以将代碼貼到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...’
增強的對象字面量
對象字面量被增強了,寫法更加簡潔與靈活,同時在定義對象的時候能夠做的事情更多了。具體表現在:
- 可以在對象字面量裡面定義原型
- 定義方法可以不用function關鍵字
- 直接調用父類方法
這樣一來,對象字面量與前面提到的類概念更加吻合,在編寫面向對象的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#等後端強類型語言的話,對此功能應該不會陌生。
//産生一個随機數
var num=Math.random();
//将這個數字輸出到console
console.log(`your num is ${num}`);
解構
自動解析數組或對象中的值。比如若一個函數要傳回多個值,正常的做法是傳回一個對象,将每個值做為這個對象的屬性傳回。但在ES6中,利用解構這一特性,可以直接傳回一個數組,然後數組中的值會自動被解析到對應接收該值的變量中。
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
不定參數
不定參數是在函數中使用命名參數同時接收不定數量的未命名參數。這隻是一種文法糖,在以前的JavaScript代碼中我們可以通過arguments變量來達到這一目的。不定參數的格式是三個句點後跟代表所有不定參數的變量名。比如下面這個例子中,…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函數本來接收三個單獨的參數人妖,人二和人三
function sayHello(people1,people2,people3){
console.log(`Hello ${people1},${people2},${people3}`);
}
//但是我們将一個數組以拓展參數的形式傳遞,它能很好地映射到每個單獨的參數
sayHello(...people);//輸出:Hello Wayou,John,Sherlock
//而在以前,如果需要傳遞數組當參數,我們需要使用函數的apply方法
sayHello.apply(null,people);//輸出:Hello Wayou,John,Sherlock
let與const 關鍵字
可以把let看成var,隻是它定義的變量被限定在了特定範圍内才能使用,而離開這個範圍則無效。const則很直覺,用來定義常量,即無法被更改值的變量。
for (let i=0;i<2;i++)console.log(i);//輸出: 0,1
console.log(i);//輸出:undefined,嚴格模式下會報錯
for of 值周遊
我們都知道for in 循環用于周遊數組,類數組或對象,ES6中新引入的for of循環功能相似,不同的是每次循環它提供的不是序号而是值。
var someArray = [ "a", "b", "c" ];
for (v of someArray) {
console.log(v);//輸出 a,b,c
}
注意,此功能google traceur并未實作,是以無法模拟調試,下面有些功能也是如此
iterator, generator
這一部分的内容有點生澀,詳情可以參見這裡。以下是些基本概念。
- iterator:它是這麼一個對象,擁有一個next方法,這個方法傳回一個對象{done,value},這個對象包含兩個屬性,一個布爾類型的done和包含任意值的value
- iterable: 這是這麼一個對象,擁有一個obj[@@iterator]方法,這個方法傳回一個iterator
- generator: 它是一種特殊的iterator。反的next方法可以接收一個參數并且傳回值取決與它的構造函數(generator function)。generator同時擁有一個throw方法
- generator 函數: 即generator的構造函數。此函數内可以使用yield關鍵字。在yield出現的地方可以通過generator的next或throw方法向外界傳遞值。generator 函數是通過function*來聲明的
- yield 關鍵字:它可以暫停函數的執行,随後可以再進進入函數繼續執行
子產品
在ES6标準中,JavaScript原生支援module了。這種将JS代碼分割成不同功能的小塊進行子產品化的概念是在一些三方規範中流行起來的,比如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);
Map,Set 和 WeakMap,WeakSet
這些是新加的集合類型,提供了更加友善的擷取屬性值的方法,不用像以前一樣用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;
有時候我們會把對象作為一個對象的鍵用來存放屬性值,普通集合類型比如簡單對象會阻止垃圾回收器對這些作為屬性鍵存在的對象的回收,有造成記憶體洩漏的危險。而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不會儲存它的值,也就是說這次添加其實沒有意思
Proxies
Proxy可以監聽對象身上發生了什麼事情,并在這些事情發生後執行一些相應的操作。一下子讓我們對一個對象有了很強的追蹤能力,同時在資料綁定方面也很有用處。
以下例子借用自這裡。
//定義被偵聽的目标對象
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);
//做一些改動來觸發代理
engineer.salary = 60;//控制台輸出:salary is changed to 60
上面代碼我已加了注釋,這裡進一步解釋。對于處理程式,是在被偵聽的對象身上發生了相應事件之後,處理程式裡面的方法就會被調用,上面例子中我們設定了set的處理函數,表明,如果我們偵聽的對象的屬性被更改,也就是被set了,那這個處理程式就會被調用,同時通過參數能夠得知是哪個屬性被更改,更改為了什麼值。
Symbols
我們知道對象其實是鍵值對的集合,而鍵通常來說是字元串。而現在除了字元串外,我們還可以用symbol這種值來做為對象的鍵。Symbol是一種基本類型,像數字,字元串還有布爾一樣,它不是一個對象。Symbol 通過調用symbol函數産生,它接收一個可選的名字參數,該函數傳回的symbol是唯一的。之後就可以用這個傳回值做為對象的鍵了。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
對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
Promises是處理異步操作的一種模式,之前在很多三方庫中有實作,比如jQuery的deferred 對象。當你發起一個異步請求,并綁定了.when(), .done()等事件處理程式時,其實就是在應用promise模式。
//建立promise
var promise = new Promise(function(resolve, reject) {
// 進行一些異步或耗時操作
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"
});
總結
總結就是一句話,前後端差異越來越小了。