ES8 走馬觀花
距離上一篇《ES6 走馬觀花》已經快兩年時間了,上個月底 ES8 正式發布,再寫一篇姊妹篇,介紹 ES8。
什麼是 ES8
ES8 是 ECMA-262 標準第 8 版的簡稱,從 ES6 開始每年發布一個版本,以年份作為名稱,因此又稱 ECMAScript 2017,簡稱 ES2017。
每年一個版本
兩個版本之間間隔時間太久(從 ES5 到 ES6 經曆了 6 年)會有以下兩個問題:
- 有很多早已討論完畢的特性需要等到標準的大版本發布才能進入標準
- 有一些特性本身比較複雜,需要較長的時間去討論。但如果推遲到下一個版本,又必須等待很長的時間才能發布
從 ES6 開始新版本發布會更頻繁,每年發布一個版本,把這一年內討論完畢的特性納入標準。
TC39 流程
TC39(Technical Committee 39)是一個推動JavaScript發展的委員會。它的成員由各個主流瀏覽器廠商的代表構成。會議的每一項決議必須大部分人讚同,並且沒有人強烈反對才可以通過。因為,對成員來說,同意就意味著有責任去實現它。每個 ECMAScript 特性都會經曆 stage 0 到 stage 4 的每一個階段。在 TC39 proposals 這個 github 倉庫可以看到每個特性的進度。
Stage 0: strawman
一種推進ECMAScript發展的自由形式,任何TC39成員,或者注冊為TC39貢獻者的會員,都可以提交。
Stage 1: proposal
該階段產生一個正式的提案。
- 確定一個帶頭人來負責該提案,帶頭人或者聯合帶頭人必須是TC39的成員。
- 描述清楚要解決的問題,解決方案中必須包含例子,API以及關於相關的語義和算法。
- 潛在問題也應該指出來,例如與其他特性的關係,實現它所麵臨的挑戰。
- polyfill和demo也是必要的。
Stage 2: draft
草案是規範的第一個版本,與最終標準中包含的特性不會有太大差別。草案之後,原則上隻接受增量修改。
- 草案中包含新增特性語法和語義的,盡可能的完善的形式說明,允許包含一些待辦事項或者占位符。
- 必須包含2個實驗性的具體實現,其中一個可以是用轉譯器實現的,例如Babel。
Stage 3: candidate
候選階段,獲得具體實現和用戶的反饋。此後,隻有在實現和使用過程中出現了重大問題才會修改。
- 規範文檔必須是完整的,評審人和ECMAScript的編輯要在規範上簽字。
- 至少要有兩個符合規範的具體實現。
Stage 4: finished
已經準備就緒,該特性會出現在年度發布的規範之中。
- 通過Test 262的驗收測試。
- 有2個通過測試的實現,以獲取使用過程中的重要實踐經驗。
- ECMAScript的編輯必須規範上的簽字。
新特性
1. String padding
新增了 String.prototype.padStart 和 String.prototype.padEnd 兩個函數,用於在字符串開頭或結尾添加填充字符串。函數的聲明如下:
String.prototype.padStart( maxLength [ , fillString ] )
String.prototype.padEnd( maxLength [ , fillString ] )
其中第一個參數是目標長度;第二個參數是填充字符串,默認是空格。示例:
'es8'.padStart(2); // 'es8'
'es8'.padStart(5); // ' es8'
'es8'.padStart(6, 'woof'); // 'wooes8'
'es8'.padStart(14, 'wow'); // 'wowwowwowwoes8'
'es8'.padStart(7, '0'); // '0000es8'
'es8'.padEnd(2); // 'es8'
'es8'.padEnd(5); // 'es8 '
'es8'.padEnd(6, 'woof'); // 'es8woo'
'es8'.padEnd(14, 'wow'); // 'es8wowwowwowwo'
'es8'.padEnd(7, '6'); // 'es86666'
典型的應用場景
使用 padStart
進行時間格式化。
'8:00'.padStart(5, '0'); // '08:00'
'18:00'.padStart(5, '0'); // '18:00'
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
使用 padStart
給命令行輸出信息對齊。
Commands:
run Start a front service
start Start a background service
stop Stop current background service
restart Restart current background service
help Display help information
感謝 left-pad 事件 為此特性的貢獻
2. Object.values & Object.entries
這兩個靜態方法是對原有的 Object.keys()
方法的補充。
const obj = {
x: 'xxx',
y: 1
};
Object.keys(obj); // ['x', 'y']
2.1 Object.values
靜態方法 Object.values()
獲取對象的所有可遍曆屬性的值,返回一個數組。示例如下:
// 基本用法
const obj = {
x: 'xxx',
y: 1
};
Object.values(obj); // ['xxx', 1]
// 數組可以看做鍵為下標的對象
// ['e', 's', '8'] -> { 0: 'e', 1: 's', 2: '8' }
const obj = ['e', 's', '8'];
Object.values(obj); // ['e', 's', '8']
// 字符串可以看做鍵為下標的對象
// 'es8' -> { 0: 'e', 1: 's', 2: '8' }
Object.values('es8'); // ['e', 's', '8']
// 如果是純 number 型的鍵值,則返回值順序根據鍵值從小到大排列
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.values(obj); // ['yyy', 'zzz', 'xxx']
2.2 Object.entries
靜態方法 Object.entries
獲取對象的雖有可遍曆屬性的鍵值對,以 [key, value] 數組的形式返回,順序和 Object.values()
一致。
// 基本用法
const obj = {
x: 'xxx',
y: 1
};
Object.entries(obj); // [['x', 'xxx'], ['y', 1]]
// 數組可以看做鍵為下標的對象
// ['e', 's', '8'] -> { 0: 'e', 1: 's', 2: '8' }
const obj = ['e', 's', '8'];
Object.entries(obj); // [['0', 'e'], ['1', 's'], ['2', '8']]
// 字符串可以看做鍵為下標的對象
// 'es8' -> { 0: 'e', 1: 's', 2: '8' }
Object.entries('es8'); // [['0', 'e'], ['1', 's'], ['2', '8']]
// 如果是純 number 型的鍵值,則返回值順序根據鍵值從小到大排列
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10': 'xxx']]
知識點展開:for...in
和 for...of
循環
上述的 Object.keys()
, Object.values()
, Object.entries()
通常用來遍曆一個對象,除了這三個方法外,常用的還有 for...in
和 for...of
+ Object.keys()
循環
使用 for...in
遍曆
const obj = {
x: 'xxx',
y: 1
};
for (let key in obj) {
console.log(key);
}
使用 for...of
+ Object.keys()
遍曆
const obj = {
x: 'xxx',
y: 1
};
for (let key of Object.keys(obj)) {
console.log(key);
}
上述例子中兩種遍曆方式等價。但在更複雜的情況下,這兩種方式的結果會不一樣。for...in
循環會遍曆對象的可枚舉屬性,包括原型鏈上繼承的屬性,而 Object.keys()
不會遍曆繼承的屬性。下麵是一個繼承的例子,Human 繼承自 Animal。
function Animal() {
this.legs = 4;
}
function Human(name) {
this.name = name;
}
Human.prototype = new Animal();
let human = new Human('es8');
使用 for...in
遍曆
for (let key in human) {
console.log(key);
}
// 'name', 'legs'
使用 for...of
+ Object.keys()
遍曆
for (let key of Object.keys(human)) {
console.log(key);
}
// 'name'
3. Object.getOwnPropertyDescriptors
靜態方法 Object.getOwnPropertyDescriptors
用於獲取對象的屬性描述符,該屬性必須是對象自己定義而不是繼承自原型鏈。結果中包含的鍵可能有 configurable、enumerable、writable、get、set 以及 value。
const obj = { es8: 'hello es8' };
Object.getOwnPropertyDescriptor(obj, 'es8');
// {
// configurable: true,
// enumerable: true,
// value: "hello es8"
// writable: true
// }
4. Trailing commas in function
ES8 標準中允許函數參數列表與調用中的尾部逗號,該特性允許我們在定義或者調用函數時添加尾部逗號。
function es8(var1, var2, var3,) {
// do something
}
es8(10, 20, 30,);
思考:在上述例子中,函數內部的 arguments.length 是 3 還是 4 ?
5. Async functions
為解決異步調用引入的 async 函數,由於 Babel 和 Nodejs 很早就支持 async 和 await 關鍵字,這個特性應該是最眾望所歸、最應用廣泛的 ES8 特性了。
Async 函數主要是從 ES6 的 generator 和 yield 進化而來,另外還得益於 TJ 大神的 co 模塊的廣泛應用,使得 JavaScript 的異步流程控製在 Async 函數進入標準之前就已經在社區經過了廣泛的實踐和討論。這裏就不對 Async 函數再做介紹了,社區裏有很多優秀的文章,大家自行搜索吧。
6. Shared memory and atomics
SharedArrayBuffer 和 Atomics 是 JavaScript 為多線程能力增加的特性,暫時使用的場景不多,更多信息可以參考這個知乎的討論: hax 的回答 —— JavaScript 如果擁有多線程能力會怎樣?,還有這篇文章對 Shared memory and atomics 介紹得很詳細 《ES proposal: Shared memory and atomics》
參考文獻
最後更新:2017-07-25 10:02:49