ECMAScript 2015 簡易教程
背景
本文最初源自es6features,你可以到github上去加星。你可以使用REPL在線預覽這些特性。
簡介
ECMAScript 6 是ECMAScript標準的最新版本,于2015年6月批準通過。ES2015是對語言的一次重大更新,是自從2009年ES5標準化后的第一次重大更新。主要的JavaScript引擎正在逐步實現這些特性,點擊這里查看瀏覽器的兼容情況。
</blockquote>查看ES2015 標準,了解關于ECMAScript 6語言的完整規范。
ECMAScript 6 新特性
箭頭函數和靜態this(Arrows and Lexical This)
箭頭函數使用胖箭頭(=>)語法,與C#,Java 8和CoffeeScript類似。支持表達式和語句作為函數體。不像普通函數,箭頭函數的this是和文法作用域綁定的。
// 表達式作為函數體 var odds = evens.map(v => v + 1); var nums = evens.map((v, i) => v + i);// 語句作為函數體 nums.forEach(v => { if (v % 5 === 0) fives.push(v); });
// 靜態this var bob = { _name: "Bob", _friends: [], printFriends() { this._friends.forEach(f => console.log(this._name + " knows " + f)); } };</pre>
類(Classes)
ES2015中的類是對基于原型面向對象模式的一個簡單語法糖。有一個單一的聲明形式可以使類模式更容易使用,并鼓勵互操作性。類支持基于原型的繼承,超類調用(super),實例方法,靜態方法和構造函數。
class SkinnedMesh extends THREE.Mesh { constructor(geometry, materials) { super(geometry, materials);this.idMatrix = SkinnedMesh.defaultMatrix(); this.bones = []; this.boneMatrices = []; //...
} update(camera) { //... super.update(); } static defaultMatrix() { return new THREE.Matrix4(); } }</pre>
增強對象字面量(Enhanced Object Literals)
對象字面量被擴展支持直接設置原型,簡潔屬性賦值和方法,超類調用。這也讓對象字面量和類聲明的關系更密切,并讓基于對象的設計更便利。
var obj = { // proto proto: theProtoObj, // 下面的寫法不會設置內部原型 'proto': somethingElse, // ‘handler: handler’的簡寫 handler, // 方法 toString() { // 調用父對象的方法 return "d " + super.toString(); }, // 屬性名是一個表達式[ "prop_" + (() => 42)() ]: 42
};</pre>
__proto__屬性需要原生支持,這一屬性在ECMAScript前一個版本中一度被廢棄。目前為止,大部分引擎支持這一屬性。同時需要注意,僅僅web瀏覽器需要實現這一屬性,在Node中現在就可使用。
模版字符串(Template Strings)
模版字符串提供構建字符串的語法糖。這類似Perl,Python等其他語言中的字符串插值。可以選擇性添加一個標簽,允許對字符串構建的定制化,避免注入攻擊或其他需求。
// 普通字符串This is a pretty little template string.
// 多行字符串
In ES5 this is not legal.
// 字符串中嵌入變量 var name = "Bob", time = "today";
Hello ${name}, how are you ${time}?
// 模版標簽 String.raw
In ES5 "\n" is a line-feed.
// 下面構造一個HTTP請求頭,來解釋差值替換和構造 GET
http://foo.org/bar?a=${a}&b=${b} Content-Type: application/json X-Credentials: ${credentials} { "foo": ${foo}, "bar": ${bar}}
(myOnReadyStateChangeHandler);</pre>解構(Destructuring)
按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱為解構。解構失敗是靜默的,類似標準的對象屬性查找foo[“bar”],找不到值則為undefined。
// 列表匹配 var [a, , b] = [1,2,3];// 對象匹配 var { op: a, lhs: { op: b }, rhs: c } = getASTNode()
// 對象匹配簡寫 // 綁定作用域中的
op
,lhs
和rhs
var {op, lhs, rhs} = getASTNode()// 函數參數 function g({name: x}) { console.log(x); } g({name: 5})
// 解構失敗,賦值為undefined var [a] = []; a === undefined;
// 如果有默認值,則為默認值 var [a = 1] = []; a === 1;</pre>
默認值 + Rest參數 + 擴展運算符(Default + Rest + Spread)
現在函數調用可以傳遞默認參數了;擴展運算符允許將數組轉化為函數的參數傳入;rest參數,用于獲取函數的多余參數,這樣就不需要使用arguments對象了。
function f(x, y=12) { // 如果不傳遞y或傳遞undefined,y的值為12 return x + y; } f(3) == 15function f(x, ...y) { // y是一個數組 return x * y.length; } f(3, "hello", true) == 6
function f(x, y, z) { return x + y + z; } // 將數組擴展為三個參數 f(...[1,2,3]) == 6</pre>
Let + Const
let類似于var,但是所聲明的變量,只在let命令所在的代碼塊內有效。const也用來聲明變量,但是聲明的是常量。一旦聲明,常量的值就不能改變。
function f() { { let x; { // 塊作用域 const x = "sneaky"; // const常量重新賦值會報錯 x = "foo"; } // let變量可以再次賦值 x = "bar"; // 在塊作用域中重復聲明將會報錯 let x = "inner"; } }Iterator和for..of循環(Iterators + For..Of)
遍歷器對象能夠自定義遍歷行為,這很像Java的Iterable。新增的for..of用來代替for..in。并不需要是數組,任何數據結構只要部署Iterator接口,就可以完成遍歷操作。
let fibonacci = { Symbol.iterator { let pre = 0, cur = 1; return { next() { [pre, cur] = [cur, pre + cur]; return { done: false, value: cur } } } } }for (var n of fibonacci) { if (n > 1000) break; console.log(n); }</pre>
遍歷器基于弱類型接口(下面是使用TypeScript語法的展示):
interface IteratorResult { done: boolean; value: any; } interface Iterator { next(): IteratorResult; } interface Iterable { [Symbol.iterator](): Iterator }需要膩子腳本(Support via polyfill)
使用遍歷器需要引用Babel膩子腳本。
Generator 函數(Generators)
Generator函數是ES6提供的一種異步編程解決方案,語法行為與傳統函數完全不同。Generator函數有多種理解角度。從語法上,首先可以把它理解成,Generator函數是一個狀態機,封裝了多個內部狀態。
執行Generator函數會返回一個遍歷器對象,也就是說,Generator函數除了狀態機,還是一個遍歷器對象生成函數。返回的遍歷器對象,可以依次遍歷Generator函數內部的每一個狀態。
形式上,Generator函數是一個普通函數,但是有兩個特征。一是,function命令與函數名之間有一個星號;二是,函數體內部使用yield語句,定義不同的內部狀態(yield語句在英語里的意思就是“產出”)。
注意:也可以使用‘await’——類似異步編程,參見ES7 await 提案。
var fibonacci = { [Symbol.iterator]: function*() { var pre = 0, cur = 1; for (;;) { var temp = pre; pre = cur; cur += temp; yield cur; } } }for (var n of fibonacci) { // 截斷1000以后的數據 if (n > 1000) break; console.log(n); }</pre>
generator接口原理如下(使用TypeScript語法的模擬):
interface Generator extends Iterator { next(value?: any): IteratorResult; throw(exception: any); }需要膩子腳本(Support via polyfill)
為了使用Generator,需要引用Babel的膩子腳本。
Unicode
ES6增強了對Unicode的支持,包括新的unicode字面量和新的正則匹配參數u模式,以及新的API來處理超過兩個字節的字符。
// ES5.1中 "??".length == 2// 新的正則參數 u "??".match(/./u)[0].length == 2
// 新的表示法 "\u{20BB7}" == "??" == "\uD842\uDFB7"
// 新字符串方法 "??".codePointAt(0) == 0x20BB7
// for-of for(var c of "??") { console.log(c); }</pre>
模塊(Modules)
語言級支持定義模塊和組件。新模塊的設計匯總了流行JavaScript模塊加載器(AMD,CommonJS)的優點。模塊的實現機制由運行時環境實現。隱式異步模型——模塊加載完成之前代碼不會執行。
// lib/math.js export function sum(x, y) { return x + y; } export var pi = 3.141593;// app.js import * as math from "lib/math"; alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js import {sum, pi} from "lib/math"; alert("2π = " + sum(pi, pi));</pre>
可以設置導出默認值和導出全部:
// lib/mathplusplus.js export * from "lib/math"; export var e = 2.71828182846; export default function(x) { return Math.exp(x); }// app.js import exp, {pi, e} from "lib/mathplusplus"; alert("2π = " + exp(pi, e));</pre>
模塊格式化
Babel可以將ES2015的模塊轉譯成幾種不同的模塊格式,包括Common.js,AMD,System和UMD。你甚至可以創建自己的模塊格式,更多細節可以查看模塊的文檔。
Map + Set + WeakMap + WeakSet
專為常見算法設計的高效數據結構。WeakMaps提供對象的弱引用作為鍵名的索引表。
// 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;
// Weak Maps var wm = new WeakMap(); wm.set(s, { extra: 42 }); wm.size === undefined
// Weak Sets var ws = new WeakSet(); ws.add({ data: 42 }); // Because the added object has no other references, it will not be held in the set</pre>
需要使用polyfill
為了使用這些新數據結構,你必須引入babel的膩子腳本。
代理(Proxies)
Proxy可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。
// 代理對象 var target = {}; var handler = { get: function (receiver, name) { returnHello, ${name}!
; } };var p = new Proxy(target, handler); p.world === "Hello, world!"; // 代理函數 var target = function () { return "I am the target"; }; var handler = { apply: function (receiver, ...args) { return "I am the proxy"; } };
var p = new Proxy(target, handler); p() === "I am the proxy";</pre>
下面是Proxy支持的攔截操作一覽:
var handler = { // target.prop get: ..., // target.prop = value set: ..., // 'prop' in target has: ..., // delete target.prop deleteProperty: ..., // target(...args) apply: ..., // new target(...args) construct: ..., // Object.getOwnPropertyDescriptor(target, 'prop') getOwnPropertyDescriptor: ..., // Object.defineProperty(target, 'prop', descriptor) defineProperty: ..., // Object.getPrototypeOf(target), Reflect.getPrototypeOf(target), // target.__proto__, object.isPrototypeOf(target), object instanceof target getPrototypeOf: ..., // Object.setPrototypeOf(target), Reflect.setPrototypeOf(target) setPrototypeOf: ..., // for (let i in target) {} enumerate: ..., // Object.keys(target) ownKeys: ..., // Object.preventExtensions(target) preventExtensions: ..., // Object.isExtensible(target) isExtensible :... }不支持的特性(Unsupported feature)
由于ES5的局限性,Proxy不能被轉換和填補。點擊這里查看瀏覽器支持情況。
Symbols
Symbol 可以作為對象的鍵值,是一種新的原始類型。Symbol函數可以接受一個字符串作為參數,表示對Symbol實例的描述,主要是為了在控制臺顯示,或者轉 為字符串時,比較容易區分。Symbol是獨一無二的,但可以通過Object.getOwnPropertySymbols獲取到。
(function() {// 塊級作用域 var key = Symbol("key");
function MyClass(privateData) { this[key] = privateData; }
MyClass.prototype = { doStuff: function() { ... this[key] ... } };
// Babel不能支持下面的操作,需要原生支持 typeof key === "symbol" })();
var c = new MyClass("hello") c["key"] === undefined</pre>
需要膩子腳本(不完備)(Limited support via polyfill)
需要使用Babel的膩子腳本,但無法全部支持。由于語言的限制,有些特性無法被轉義和填補。更多細節請查看core.js的相關說明。
繼承內建父類(Subclassable Built-ins)
在ES2015中,語言內置父類如Array,Date和Dom元素能夠被子類繼承。
// 子類繼承Array class MyArray extends Array { constructor(...args) { super(...args); } }var arr = new MyArray(); arr[1] = 12; arr.length == 2</pre>
部分支持(Partial support)
能夠子類化的父類必須是基于類的,比如HTMLElement能被子類繼承,然后由于ES5引擎的限制許多父類不能被子類化,如Date,Array和Error等。
Math + Number + String + Object APIs
ES2015擴展了很多新的API,包括核心數學函數,數組轉換接口和實現對象復制功能的Object.assign。
Number.EPSILON Number.isInteger(Infinity) // false Number.isNaN("NaN") // falseMath.acosh(3) // 1.762747174039086 Math.hypot(3, 4) // 5 Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes("cd") // true "abc".repeat(3) // "abcabcabc"
Array.from(document.querySelectorAll("*")) // 返回一個真正的數組 Array.of(1, 2, 3) // 類似new Array(...),但只有一個參數時語義不變 [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) })</pre>
需要使用膩子腳本(不能完全模擬)(Limited support from polyfill)
通過Babel的膩子腳本,能夠支持大部分API,然而,由于各種原因(例如,String.prototype.normalize需要添加大量代碼)并未全部支持。你可以點擊這里找到更多膩子腳本。
二進制和八進制字面量(Binary and Octal Literals)
新增了二進制和八進制字面量表示法(注意和原來的ES5廢棄的八進制表示法不同)。
0b111110111 === 503 // true 0o767 === 503 // true僅支持字面量形式(Only supports literal form)
Babel僅能轉換0o767,不支持Numver("0o767")。
Promises
所謂Promise,就是一個對象,用來傳遞異步操作的消息。它代表了某個未來才會知道結果的事件(通常是一個異步操作),并且這個事件提供統一的API,可供進一步處理。目前JavaScript庫已在使用中。
function timeout(duration = 0) { return new Promise((resolve, reject) => { setTimeout(resolve, duration); }) }var p = timeout(1000).then(() => { return timeout(2000); }).then(() => { throw new Error("hmm"); }).catch(err => { return Promise.all([timeout(100), timeout(200)]); })</pre>
需要使用膩子腳本(Support via polyfill)
要想使用Promises需要引用Babel膩子腳本。
反射(Reflect API)
Reflect對象與Proxy對象一樣,也是ES6為了操作對象而提供的新API。
var O = {a: 1}; Object.defineProperty(O, 'b', {value: 2}); O[Symbol('c')] = 3;Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]
function C(a, b){ this.c = a + b; } var instance = Reflect.construct(C, [20, 22]); instance.c; // 42</pre>
需要使用膩子腳本(Support via polyfill)
Reflect API需要引用babel 膩子腳本。
尾調用(Tail Calls)
尾調用(Tail Call)是函數式編程的一個重要概念,本身非常簡單,一句話就能說清楚,就是指某個函數的最后一步是調用另一個函數。
對于尾遞歸來說,由于只存在一個調用幀,所以永遠不會發生“棧溢出”錯誤。
function factorial(n, acc = 1) { "use strict"; if (n <= 1) return acc; return factorial(n - 1, n * acc); }// 現在的實現大部分都會移除,但在ES2015中不會 factorial(100000)</pre>
原文鏈接
https://babeljs.io/docs/learn-es2015/
更多資料
- ECMAScript 6 入門
- 深入淺出ES6
</ul> </div>
顏海鏡的博客 由 顏海鏡 創作,采用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基于http://yanhaijing.com上的作品創作。原文網址:http://yanhaijing.com/javascript/2015/09/11/learn-es2015
感謝您的閱讀,如果您對我的博客所講述的內容有興趣,請繼續關注我的后續博客
開源項目
- lodJS JavaScript模塊加載器,基于AMD。
- data.js 是帶有消息通知的數據中心,我把她稱為活的數據。
- template.js 一款javascript模板引擎,簡單,好用。
- 變色方塊 是一款休閑益智小游戲,適合所有人群。
- 查看更多
</ul>捐贈
![]()