ES8 蜻蜓點水(ECMAScript2017 新特性)

距離上一篇《ES6 蜻蜓點水》已經快兩年時間了,上個月底 ES8 正式發佈,再寫一篇姊妹篇,介紹 ES8 新特性。javascript

什麼是 ES8

ES8 是 ECMA-262 標準第 8 版的簡稱,從 ES6 開始每一年發佈一個版本,以年份做爲名稱,所以又稱 ECMAScript 2017,簡稱 ES2017。html

每一年一個版本

兩個版本之間間隔時間過久(從 ES5 到 ES6 經歷了 6 年)會有如下兩個問題:java

  • 有不少早已討論完畢的特性須要等到標準的大版本發佈才能進入標準git

  • 有一些特性自己比較複雜,須要較長的時間去討論。但若是推遲到下一個版本,又必須等待很長的時間才能發佈es6

從 ES6 開始新版本發佈會更頻繁,每一年發佈一個版本,把這一年內討論完畢的特性歸入標準。github

TC39 流程

TC39(Technical Committee 39)是一個推進JavaScript發展的委員會。它的成員由各個主流瀏覽器廠商的表明構成。會議的每一項決議必須大部分人贊同,而且沒有人強烈反對才能夠經過。由於,對成員來講,贊成就意味着有責任去實現它。每一個 ECMAScript 特性都會經歷 stage 0 到 stage 4 的每個階段。在 TC39 proposals 這個 github 倉庫能夠看到每一個特性的進度。算法

Stage 0: strawman

一種推動ECMAScript發展的自由形式,任何TC39成員,或者註冊爲TC39貢獻者的會員,均可以提交。npm

Stage 1: proposal

該階段產生一個正式的提案。segmentfault

  1. 肯定一個帶頭人來負責該提案,帶頭人或者聯合帶頭人必須是TC39的成員。數組

  2. 描述清楚要解決的問題,解決方案中必須包含例子,API以及關於相關的語義和算法。

  3. 潛在問題也應該指出來,例如與其餘特性的關係,實現它所面臨的挑戰。

  4. polyfill和demo也是必要的。

Stage 2: draft

草案是規範的第一個版本,與最終標準中包含的特性不會有太大差異。草案以後,原則上只接受增量修改。

  1. 草案中包含新增特性語法和語義的,儘量的完善的形式說明,容許包含一些待辦事項或者佔位符。

  2. 必須包含2個實驗性的具體實現,其中一個能夠是用轉譯器實現的,例如Babel。

Stage 3: candidate

候選階段,得到具體實現和用戶的反饋。此後,只有在實現和使用過程當中出現了重大問題纔會修改。

  1. 規範文檔必須是完整的,評審人和ECMAScript的編輯要在規範上簽字。

  2. 至少要有兩個符合規範的具體實現。

Stage 4: finished

已經準備就緒,該特性會出如今年度發佈的規範之中。

  1. 經過Test 262的驗收測試。

  2. 有2個經過測試的實現,以獲取使用過程當中的重要實踐經驗。

  3. ECMAScript的編輯必須規範上的簽字。

新特性

1. String padding

新增了 String.prototype.padStartString.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() 一致。
example.png

// 基本用法
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...infor...of 循環

上述的 Object.keys(), Object.values(), Object.entries() 一般用來遍歷一個對象,除了這三個方法外,經常使用的還有 for...infor...of + Object.keys() 循環

example.png

使用 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》

參考文獻

相關文章
相關標籤/搜索