介紹 ES6,也叫ECMAScript2015(以下統稱ES6),是ECMAScript標準的最新版本。這個標準在2015年6月份被正式批准。ES6是js語言很有意義的一次更新,也是2009年ES5被標準化以來第一次重大的更新。主流javascript引擎中的這些新特性正在開發中。 ES6特性完整版 ...
介紹
ES6,也叫ECMAScript2015(以下統稱ES6),是ECMAScript標準的最新版本。這個標準在2015年6月份被正式批准。ES6是js語言很有意義的一次更新,也是2009年ES5被標準化以來第一次重大的更新。主流javascript引擎中的這些新特性正在開發中。
ES6特性完整版參見:ES6特性完整版說明
ES6有以下這些新特性:
箭頭函數
使用=>表示function的簡略版。這和C#,Java8,CoffeeScript的語法很像。它可以支持聲明體和表達式體,並且表達式體會有一個返回值。不像普通函數,箭頭函數和其上下文共用同一個詞法作用域的this。
(譯者註:詞法作用域即“靜態作用域”,)
1 // Expression bodies 表達式體 2 var odds = evens.map(v => v + 1); 3 /*v=>v+1相當於function(v){return v+1;}*/ 4 var nums = evens.map((v, i) => v + i); 5 var pairs = evens.map(v => ({even: v, odd: v + 1})); 6 7 // Statement bodies 聲明體 8 nums.forEach(v => { 9 if (v % 5 === 0) 10 fives.push(v); 11 }); 12 /*相當於nums.forEach(function (v) { 13 if (v % 5 === 0) fives.push(v); 14 });*/ 15 16 // Lexical this 詞法作用域的this 17 var bob = { 18 _name: "Bob", 19 _friends: [], 20 printFriends() { 21 this._friends.forEach(f => 22 console.log(this._name + " knows " + f)); 23 } 24 }
類
ES2015的classes是在基於原型的面向對象模式中的一個簡單的語法糖。它有一個簡單的聲明形式,使得類模式更易於使用,並且這種方式有益於互用性。classes支持基於原型的繼承、父類方法的調用、實例化和靜態方法、構造函數。
(譯者註:超級像後端語言。)
1 class SkinnedMesh extends THREE.Mesh {//使用extends來表示繼承 2 constructor(geometry, materials) { 3 super(geometry, materials);//父類的構造函數 4 5 this.idMatrix = SkinnedMesh.defaultMatrix(); 6 this.bones = []; 7 this.boneMatrices = []; 8 //... 9 } 10 update(camera) { 11 //... 12 super.update(); 13 } 14 get boneCount() { 15 return this.bones.length; 16 } 17 set matrixType(matrixType) { 18 this.idMatrix = SkinnedMesh[matrixType](); 19 } 20 static defaultMatrix() {//靜態方法 21 return new THREE.Matrix4(); 22 } 23 }
增強的對象字面量
對象字面量被擴展成可以在構造階段設置原型、變數名和變數值相同時可以使用簡寫形式(如foo:foo 可以寫成foo)、定義方法、調用父方法、動態計算變數名。同時,這種方式也和上一小節說的類(class)聲明十分吻合,也使得基於對象的設計更加便利。
1 var obj = { 2 // __proto__ obj的原型 3 __proto__: theProtoObj, 4 // Shorthand for ‘handler: handler’ 簡寫,翻譯過來就是handler : handler, 5 handler, 6 // Methods 7 toString() { 8 // Super calls 調用父類的函數 9 return "d " + super.toString(); 10 }, 11 // Computed (dynamic) property names 需要計算的動態屬性名稱 12 [ 'prop_' + (() => 42)() ]: 42 13 };
字元串模板
字元串模板提供了一個創建字元串的語法糖。這和perl、python等等語言中的字元串插入功能很類似。有選擇性的,它允許在字元串中插入一個標簽(tag)使得字元串的構建是可定製化的,這可以避免在字元串里進行註入攻擊,或者構造更高級別的結構。
1 // Basic literal string creation 基本的字元串字面量方式創建 2 `In JavaScript '\n' is a line-feed.` 3 4 // Multiline strings 多行字元串 5 `In JavaScript this is 6 not legal.` 7 8 // String interpolation 插入變數 9 var name = "Bob", time = "today"; 10 `Hello ${name}, how are you ${time}?` 11 12 // Construct an HTTP request prefix is used to interpret the replacements and construction 13 // 構建一個通用的http請求首碼,其中欄位值可以動態替換 14 POST`http://foo.org/bar?a=${a}&b=${b} 15 Content-Type: application/json 16 X-Credentials: ${credentials} 17 { "foo": ${foo}, 18 "bar": ${bar}}`(myOnReadyStateChangeHandler);
解構(解析結構)
解構支持使用模式匹配來匹配數組和對象。結構是弱化失敗的,類似於在一個標準的對象中查找foo[“bar”]屬性,如果沒有找到只是返回undefined。
(譯者註:fail-soft-字面意思是弱化失敗,再這裡是指如果解析結構解析不出來不會拋異常,只是返回一個undefined或預設值)
1 // list matching 匹配數組 2 var [a, , b] = [1,2,3]; 3 4 // object matching 匹配對象 5 var { op: a, lhs: { op: b }, rhs: c } 6 = getASTNode() 7 8 // object matching shorthand 匹配對象的一部分 9 // binds `op`, `lhs` and `rhs` in scope 例如getASTNode()的返回值為{op,op2,lhs,rhs},這個表達式將會自動匹配op,lhs,rhs 10 /*稍微有點難懂,下麵的語句翻譯過來就是 11 var _getASTNode = getASTNode(); 12 13 var op = _getASTNode.op; 14 var lhs = _getASTNode.lhs; 15 var rhs = _getASTNode.rhs; 16 */ 17 var {op, lhs, rhs} = getASTNode() 18 19 // Can be used in parameter position 可以作為參數使用 20 function g({name: x}) { 21 console.log(x); 22 } 23 g({name: 5}) 24 25 // Fail-soft destructuring 弱化解析失敗,返回undefined 26 var [a] = []; 27 a === undefined; 28 29 // Fail-soft destructuring with defaults 有預設值時弱化解析失敗,返回undefiend 30 var [a = 1] = []; 31 a === 1;
預設值+不定參數+參數展開
預設值:支持由被調用函數設置的參數預設值。
參數展開:在函數調用時實參使用...運算符,可以將作為參數的數組拆解為連續的多個參數。 在函數定義時虛參使用...運算符,則可以將函數尾部的多個參數綁定到一個數組中。
不定參數:不定參數取代了傳統的參數,並可更直接地應用於通常的用例中。
1 //預設值 2 function f(x, y=12) { 3 // y is 12 if not passed (or passed as undefined) 如果y沒有實參,則賦值為預設值12 4 return x + y; 5 } 6 f(3) == 15
1 //不定個數的參數 2 function f(x, ...y) { 3 // y is an Array 有...,表示y是一個數組 4 return x * y.length; 5 } 6 f(3, "hello", true) == 6
1 //參數展開 2 function f(x, y, z) { 3 return x + y + z; 4 } 5 // Pass each elem of array as argument ...表示在數組中的每一個元素分別按順序對應一個入參 6 f(...[1,2,3]) == 6
let和const關鍵字
兩個關鍵字都具有塊級作用域,只在塊級作用域中生效。let是一種新的var。(譯者註:let可以看成var,它定義的變數被限制在特定範圍中才能使用,離開這個範圍就自動銷毀)。const只能一次性聲明(譯者註:不能修改其值)。兩者靜態限制防止了在賦值前使用變數。
1 function f() { 2 { 3 let x; 4 { 5 // okay, block scoped name 不報錯 6 const x = "sneaky"; 7 // error, const 報錯,常量不允許修改值 8 x = "foo"; 9 } 10 // error, already declared in block 報錯,在該塊級作用域中已經聲明瞭 11 let x = "inner"; 12 } 13 }
Iterator+For..Of 迭代器
可以像CLR IEnumerable或JAVA Utterable一樣自定義迭代器。將for..in 變為自定義且基於迭代器的for..of。不需要通過數組實現,可以像LINQ一樣使用懶惰模式。
1 let fibonacci = { 2 [Symbol.iterator]() { 3 let pre = 0, cur = 1; 4 return { 5 next() { 6 [pre, cur] = [cur, pre + cur]; 7 return { done: false, value: cur } 8 } 9 } 10 } 11 } 12 13 for (var n of fibonacci) { 14 // truncate the sequence at 1000 實現從1到1000的斐波那契數列計算 15 if (n > 1000) 16 break; 17 console.log(n); 18 }
迭代器是基於這些鴨子類型的介面(使用TypeScript類型語法,僅僅用於闡述問題)
1 interface IteratorResult { 2 done: boolean; 3 value: any; 4 } 5 interface Iterator { 6 next(): IteratorResult; 7 } 8 interface Iterable { 9 [Symbol.iterator](): Iterator 10 }
Generators 生成器
Generators 通過使用function和yield簡化了迭代器編寫。函數聲明時如果是function 形式的會返回一個Generator實例。Generator是迭代器iterators的子類,還包括了額外的next和throw方法。這允許了值可以迴流到生成器中,所以yield是返回一個值或者拋異常的一個表達式。
1 var fibonacci = { 2 [Symbol.iterator]: function*() { 3 var pre = 0, cur = 1; 4 for (;;) { 5 var temp = pre; 6 pre = cur; 7 cur += temp; 8 yield cur; 9 } 10 } 11 } 12 13 for (var n of fibonacci) { 14 // truncate the sequence at 1000 15 if (n > 1000) 16 break; 17 console.log(n); 18 }
生成器介面如下(此處使用TypeScript 的類型語法,僅用於闡述問題):
1 interface Generator extends Iterator { 2 next(value?: any): IteratorResult; 3 throw(exception: any); 4 }
Unicode
新增加的特性不影響老功能的使用,字元串將有新的文本格式,也增加了正則 u 模式來處理碼位。同時,新的api可以在21碼位級別上處理字元串。在javascript中,這些新增點可以支持構建全球化應用。
1 // same as ES5.1 2 // 與 ES5.1 相同 3 "吉".length == 2 4 5 // new RegExp behaviour, opt-in ‘u’ 6 // 使用可選的‘u’修飾符表示正則 7 "吉".match(/./u)[0].length == 2 8 9 // new form 10 // 左邊是新寫法,右邊是舊寫法。新的形式可以通過添加一組大括弧`{}`來表示超過四位元組的碼點 11 "\u{20BB7}"=="吉"=="\uD842\uDFB7" 12 13 // new String ops 14 "吉".codePointAt(0) == 0x20BB7 15 16 // for-of iterates code points 17 // 以碼位為單位進行迭代 18 for(var c of "吉") { 19 console.log(c); 20 }
模塊 modules
在語言層面上支持使用模塊來定義組件。將流行的javascript模塊載入器(AMD,CommonJS)的模式編成規範。運行時的行為由宿主定義的預設載入器決定。隱式非同步模型- (當前模塊)沒有代碼會執行,直到被請求的模塊可用且處理過。
1 // lib/math.js 2 export function sum(x, y) { //export 表示可以被載入 3 return x + y; 4 } 5 export var pi = 3.141593;
1 // app.js 2 import * as math from "lib/math"; //import 表示被導入 3 alert("2π = " + math.sum(math.pi, math.pi));
1 // otherApp.js 2 import {sum, pi} from "lib/math"; 3 console.log("2π = " + sum(pi, pi));
額外的新增的特征包括export default和export * ;
1 // lib/mathplusplus.js 2 export * from "lib/math"; 3 export var e = 2.71828182846; 4 export default function(x) {//註意default關鍵字表示預設 5 return Math.log(x); 6 }
1 // app.js 2 import exp, {pi, e} from "lib/mathplusplus"; 3 console.log("e^π = " + exp(pi));
Map + Set + WeakMap + WeakSet
這四個對於常見的演算法來說是很有用的數據結構。weakMaps這一數據結構提供的索引表,擁有不會發生記憶體泄露的對象鍵名。(註:指的是當對象作為鍵名時,對象可能會被回收,回收後自動移除所在鍵值對)。
1 // Sets 2 var s = new Set(); 3 s.add("hello").add("goodbye").add("hello"); 4 s.size === 2; 5 s.has("hello") === true; 6 7 // Maps 8 var m = new Map(); 9 m.set("hello", 42); 10 m.set(s, 34); 11 m.get(s) == 34; 12 13 // Weak Maps 14 var wm = new WeakMap(); 15 wm.set(s, { extra: 42 }); 16 wm.size === undefined 17 18 // Weak Sets 19 var ws = new WeakSet(); 20 ws.add({ data: 42 }); 21 // Because the added object has no other references, it will not be held in the set 22 //因為這個新的對象沒有其他引用,所以不會真的添加到set
Proxies 代理
代理創建的對象可以獲得宿主對象的全部行為(屬性和方法)。可以用於攔截,虛擬對象,日誌/分析等。
1 // Proxying a normal object 代理一個普通對象 2 var target = {};//被代理的類 3 var handler = {//第二個參數表示攔截後的操作 4 get: function (receiver, name) {//這裡是攔截獲取屬性的操作:p.world 相當於第二個參數name為world 5 return `Hello, ${name}!`; 6 } 7 }; 8 9 var p = new Proxy(target, handler); 10 p.world === 'Hello, world!';
1 // Proxying a function object 代理一個函數對象 2 var target = function () { return 'I am the target'; }; 3 var handler = { 4 apply: function (receiver, ...args) { 5 return 'I am the proxy'; 6 } 7 }; 8 9 var p = new Proxy(target, handler); 10 p() === 'I am the proxy';
這裡是所有運行時級別的元數據操作陷阱:
1 var handler = 2 { 3 get:..., 4 set:..., 5 has:..., 6 deleteProperty:..., 7 apply:..., 8 construct:..., 9 getOwnPropertyDescriptor:..., 10 defineProperty:..., 11 getPrototypeOf:..., 12 setPrototypeOf:..., 13 enumerate:..., 14 ownKeys:..., 15 preventExtensions:..., 16 isExtensible:... 17 }
Symbols 符號
symbol有許可權控制對象狀態。symbol允許通過String(與ES5相同)或symbol作為鍵來訪問對象的屬性。symbol是一個新的原始類型。可選的name參數用於調試-但是它不是符號身份的一部分。符號是唯一的(就像gensym),但是只要他們通過像Object.getOwnPropertySymbles這樣的反射特性暴露出來,就不是私有的。
1 var MyClass = (function() { 2 3 // module scoped symbol 4 var key = Symbol("key"); 5 6 function MyClass(privateData) { 7 this[key] = privateData; 8 } 9 10 MyClass.prototype = { 11 doStuff: function() { 12 ... this[key] ... 13 } 14 }; 15 16 return MyClass; 17 })(); 18 19 var c = new MyClass("hello") 20 c["key"] === undefined//已經跳出了作用域,所以是undefined
內建對象可擁有子類
在ES6中,像Array,Date和Dom元素這樣的內建對象都可以被子類化。
1 // User code of Array subclass 2 class MyArray extends Array { 3 constructor(...args) { super(...args); } 4 } 5 6 var arr = new MyArray(); 7 arr[1] = 12; 8 arr.length == 2
Math + Number + String + Object APIs
加了一些新的api,包括math的核心庫,數組轉換幫助函數,和用來複制對象的Object.assign。
1 Number.EPSILON 2 Number.isInteger(Infinity) // false 是否是整形 3 Number.isNaN("NaN") // false 是否是非法數字 4 5 Math.acosh(3) // 1.762747174039086 餘弦 6 Math.hypot(3, 4) // 5 直角三角形的斜邊 7 Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2 帶符號的懲罰 8 9 "abcde".includes("cd") // true 10 "abc".repeat(3) // "abcabcabc" 11 12 Array.from(document.querySelectorAll('*')) // Returns a real Array 13 Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior 14 [0, 0, 0].fill(7, 1) // [0,7,7] 15 [1, 2, 3].find(x => x == 3) // 3 16 [1, 2, 3].findIndex(x => x == 2) // 1 17 [1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2] 18 ["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"] 19 ["a", "b", "c"].keys() // iterator 0, 1, 2 20 ["a", "b", "c"].values() // iterator "a", "b", "c" 21 22 Object.assign(Point, { origin: new Point(0,0) }) //拷貝對象
Binary and Octal Literals
增加了兩個新的數字進位標識符,第二個字母為b來表示二進位,第二個字母為o來表示八進位。
1 0b111110111 === 503 // true 二進位 2 0o767 === 503 // true 八進位
Promises
Promises是處理非同步操作的一種模式。 Promises是第一個能代表未來能用到的值的類。Promises已經被使用在很多JavaScript第三庫中。
1 function timeout(duration = 0) { 2 return new Promise((resolve, reject) => { 3 setTimeout(resolve, duration); 4 }) 5 } 6 7 var p = timeout(1000).then(() => { 8 return timeout(2000); 9 }).then(() => { 10 throw new Error("hmm"); 11 }).catch(err => { 12 return Promise.all([timeout(100), timeout(200)]); 13 })
Reflect API
完整的反射API暴露了對象在運行時的元操作。這類似於一個反向代理,並允許調用與代理陷阱中相同的元操作。實現代理非常有用。
1 var O = {a: 1}; 2 Object.defineProperty(O, 'b', {value: 2}); 3 O[Symbol('c')] = 3; 4 5 Reflect.ownKeys(O); // ['a', 'b', Symbol(c)] 6 7 function C(a, b){ 8 this.c = a + b; 9 } 10 var instance = Reflect.construct(C, [20, 22]); 11 instance.c; // 42
Tail Calls
保證尾部調用時棧區不會沒有限制的增長,這使得遞歸函數及時在沒有限制的輸入時也能保證安全性。
1 function factorial(n, acc = 1) { 2 'use strict'; 3 if (n <= 1) return acc; 4 return factorial(n - 1, n * acc); 5 } 6 // Stack overflow in most implementations today, 棧溢出在現在經常存在 7 // but safe on arbitrary inputs in ES6 但是在ES6中卻很安全 8 factorial(100000)