細(xì)解JavaScript ES7 ES8 ES9 新特性

導(dǎo)言:ECMAScript的演化不會停止榕吼,但是我們完全沒必要害怕饿序。除了ES6這個史無前例的版本帶來了海量的信息和知識點(diǎn)以外,之后每年一發(fā)的版本都僅僅帶有少量的增量更新羹蚣,一年更新的東西花半個小時就能搞懂了原探,完全沒必要畏懼。本文將帶您花大約一個小時左右的時間,迅速過一遍ES7,ES8,ES9的新特性咽弦。

想追求更好的閱讀體驗徒蟆,請移步原文地址

es_16_17_18

題記:本文提供了一個在線PPT版本,方便您瀏覽 細(xì)解JAVASCRIPT ES7 ES8 ES9 新特性 在線PPT ver

本文的大部分內(nèi)容譯自作者Axel Rauschmayer博士的網(wǎng)站型型,想了解更多關(guān)于作者的信息段审,可以瀏覽Exploring JS: JavaScript books for programmers

那些與ECMAScript有關(guān)的事情

誰在設(shè)計ECMAScript?

TC39 (Technical Committee 39)。

TC39 是推進(jìn) JavaScript 發(fā)展的委員會闹蒜。其會員都是公司(其中主要是瀏覽器廠商)寺枉。TC39 定期召開會議,會議由會員公司的代表與特邀專家出席绷落。會議紀(jì)錄都可在網(wǎng)上查看姥闪,可以讓你對 TC39 如何工作有一個清晰的概念。

很有意思的是砌烁,TC39 實(shí)行的是協(xié)商一致的原則:通過一項決議必須得到每一位會員(公司代表)的贊成筐喳。

ECMAScript的發(fā)布周期

在2015年發(fā)布的 ECMAScript(ES6)新增內(nèi)容很多,在 ES5 發(fā)布近 6 年(2009-11 至 2015-6)之后才將其標(biāo)準(zhǔn)化函喉。兩個發(fā)布版本之間時間跨度如此之大主要有兩大原因:

  • 比新版率先完成的特性避归,必須等待新版的完成才能發(fā)布。
  • 那些需要花長時間完成的特性管呵,也頂著很大的壓力被納入這一版本梳毙,因為如果推遲到下一版本發(fā)布意味著又要等很久,這種特性也會推遲新的發(fā)布版本撇寞。

因此顿天,從 ECMAScript 2016(ES7)開始,版本發(fā)布變得更加頻繁蔑担,每年發(fā)布一個新版本牌废,這么一來新增內(nèi)容也會更小。新版本將會包含每年截止時間之前完成的所有特性啤握。

ECMAScript的發(fā)布流程

每個 ECMAScript 特性的建議將會從階段 0 開始鸟缕, 然后經(jīng)過下列幾個成熟階段。其中從一個階段到下一個階段必須經(jīng)過 TC39 的批準(zhǔn)排抬。

  1. stage-0 - Strawman: just an idea, possible Babel plugin.
    任何討論懂从、想法、改變或者還沒加到提案的特性都在這個階段蹲蒲。只有TC39成員可以提交番甩。

當(dāng)前的stage 0列表可以查看這里 --> Stage 0 Proposals

  1. stage-1 - Proposal: this is worth working on.

    什么是 Proposal?一份新特性的正式建議文檔届搁。提案必須指明此建議的潛在問題缘薛,例如與其他特性之間的關(guān)聯(lián)窍育,實(shí)現(xiàn)難點(diǎn)等。

  2. stage-2 - Draft: initial spec.

    什么是 Draft宴胧?草案是規(guī)范的第一個版本漱抓。其與最終標(biāo)準(zhǔn)中包含的特性不會有太大差別。

草案之后恕齐,原則上只接受增量修改乞娄。這個階段開始實(shí)驗如何實(shí)現(xiàn),實(shí)現(xiàn)形式包括polyfill, 實(shí)現(xiàn)引擎(提供草案執(zhí)行本地支持)显歧,或者編譯轉(zhuǎn)換(例如babel)

  1. stage-3 - Candidate: complete spec and initial browser implementations.

候選階段仪或,獲得具體實(shí)現(xiàn)和用戶的反饋。此后追迟,只有在實(shí)現(xiàn)和使用過程中出現(xiàn)了重大問題才會修改溶其。至少要在一個瀏覽器中實(shí)現(xiàn)骚腥,提供polyfill或者babel插件敦间。

  1. stage-4 - Finished: will be added to the next yearly release.

已經(jīng)準(zhǔn)備就緒,該特性會出現(xiàn)在下個版本的ECMAScript規(guī)范之中束铭。

當(dāng)前的stage 1-3列表可以查看這里 --> ECMAScript proposals

已經(jīng)正式發(fā)布的特性索引

Proposal Author Champion(s) TC39 meeting notes Expected Publication Year
Array.prototype.includes Domenic Denicola Domenic Denicola<br />Rick Waldron November 2015 2016
Exponentiation operator Rick Waldron Rick Waldron January 2016 2016
Object.values/Object.entries Jordan Harband Jordan Harband March 2016 2017
String padding Jordan Harband Jordan Harband<br />Rick Waldron May 2016 2017
Object.getOwnPropertyDescriptors Jordan Harband<br />Andrea Giammarchi Jordan Harband<br />Andrea Giammarchi May 2016 2017
Trailing commas in function parameter lists and calls Jeff Morrison Jeff Morrison July 2016 2017
Async functions Brian Terlson Brian Terlson July 2016 2017
Shared memory and atomics Lars T Hansen Lars T Hansen January 2017 2017
Lifting template literal restriction Tim Disney Tim Disney March 2017 2018
s (dotAll) flag for regular expressions Mathias Bynens Brian Terlson<br />Mathias Bynens November 2017 2018
RegExp named capture groups Gorkem Yakin<br />Daniel Ehrenberg Daniel Ehrenberg<br />Brian Terlson<br />Mathias Bynens November 2017 2018
Rest/Spread Properties Sebastian Markb?ge Sebastian Markb?ge January 2018 2018
RegExp Lookbehind Assertions Gorkem Yakin<br />Nozomu Katō<br />Daniel Ehrenberg Daniel Ehrenberg<br />Mathias Bynens January 2018 2018
RegExp Unicode Property Escapes Mathias Bynens Brian Terlson<br />Daniel Ehrenberg<br />Mathias Bynens January 2018 2018
Promise.prototype.finally Jordan Harband Jordan Harband January 2018 2018
Asynchronous Iteration Domenic Denicola Domenic Denicola January 2018 2018
Optional catch binding Michael Ficarra Michael Ficarra May 2018 2019
JSON superset Richard Gibson Mark Miller<br />Mathias Bynens May 2018 2019

ES7新特性(ECMAScript 2016)

ECMAScript 2016

ES7在ES6的基礎(chǔ)上主要添加了兩項內(nèi)容:

  • Array.prototype.includes()方法
  • 求冪運(yùn)算符(**)

Array.prototype.includes()方法

includes() 方法用來判斷一個數(shù)組是否包含一個指定的值廓块,根據(jù)情況,如果包含則返回 true契沫,否則返回false带猴。

var array = [1, 2, 3];

console.log(array.includes(2));
// expected output: true

var pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// expected output: true

console.log(pets.includes('at'));
// expected output: false

Array.prototype.includes()方法接收兩個參數(shù):

  • 要搜索的值
  • 搜索的開始索引。

當(dāng)?shù)诙€參數(shù)被傳入時懈万,該方法會從索引處開始往后搜索(默認(rèn)索引值為0)拴清。若搜索值在數(shù)組中存在則返回true,否則返回false会通。 且看下面示例:

['a', 'b', 'c', 'd'].includes('b')         // true
['a', 'b', 'c', 'd'].includes('b', 1)      // true
['a', 'b', 'c', 'd'].includes('b', 2)      // false

乍一看口予,includes的作用跟數(shù)組的indexOf重疊,為什么要特意增加這么一個api呢涕侈?主要區(qū)別有以下幾點(diǎn):

  • 返回值沪停。看一個函數(shù)裳涛,先看他們的返回值木张。indexOf的返回數(shù)是值型的,includes的返回值是布爾型端三,所以在if條件判斷的時候includes要簡單得多舷礼,而indexOf 需要多寫一個條件進(jìn)行判斷。
var ary = [1];
if (ary.indexOf(1) !== -1) {
    console.log("數(shù)組存在1")
}
if (ary.includes(1)) {
    console.log("數(shù)組存在1")
}
  • NaN的判斷郊闯。如果數(shù)組中有NaN妻献,你又正好需要判斷數(shù)組是否有存在NaN浮声,這時你使用indexOf是無法判斷的,你必須使用includes這個方法旋奢。
var ary1 = [NaN];
console.log(ary1.indexOf(NaN))//-1
console.log(ary1.includes(NaN))//true
  • 當(dāng)數(shù)組的有空的值的時候泳挥,includes會認(rèn)為空的值是undefined,而indexOf不會至朗。
var ary1 = new Array(3);
console.log(ary1.indexOf(undefined));//-1
console.log(ary1.includes(undefined))//true

求冪運(yùn)算符(**)

加/減法我們通常都是用其中綴形式屉符,直觀易懂。在ECMAScript2016中锹引,我們可以使用**來替代Math.pow矗钟。

4 ** 3           // 64

效果等同于

Math.pow(4,3)

值得一提的是,作為中綴運(yùn)算符嫌变,**還支持以下操作

let n = 4;
n **= 3;
// 64

ES8新特性(ECMAScript 2017)

ECMAScript 2017

在2017年1月的TC39會議上吨艇,ECMAScript 2017的最后一個功能“Shared memory and atomics”推進(jìn)到第4階段。這意味著它的功能集現(xiàn)已完成腾啥。

ECMAScript 2017特性一覽

主要新功能:

  • 異步函數(shù) Async Functions(Brian Terlson)
  • 共享內(nèi)存和Atomics(Lars T. Hansen)

次要新功能:

  • Object.values / Object.entries(Jordan Harband)
  • String padding(Jordan Harband东涡,Rick Waldron)
  • Object.getOwnPropertyDescriptors() (Jordan Harband,Andrea Giammarchi)
  • 函數(shù)參數(shù)列表和調(diào)用中的尾逗號(Jeff Morrison)

Async Functions

Async Functions也就是我們常說的Async/Await倘待,相信大家對于這個概念都已經(jīng)不陌生了疮跑。Async/Await是一種用于處理JS異步操作的語法糖,可以幫助我們擺脫回調(diào)地獄凸舵,編寫更加優(yōu)雅的代碼祖娘。

通俗的理解,async關(guān)鍵字的作用是告訴編譯器對于標(biāo)定的函數(shù)要區(qū)別對待啊奄。當(dāng)編譯器遇到標(biāo)定的函數(shù)中的await關(guān)鍵字時渐苏,要暫時停止運(yùn)行,帶到await標(biāo)定的函數(shù)處理完畢后菇夸,再進(jìn)行相應(yīng)操作琼富。如果該函數(shù)fulfiled了,則返回值是fulfillment value峻仇,否則得到的就是reject value公黑。

下面通過拿普通的promise寫法來對比,就很好理解了:

async function asyncFunc() {
    const result = await otherAsyncFunc();
    console.log(result);
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc()
    .then(result => {
        console.log(result);
    });
}

按順序處理多個異步函數(shù)的時候優(yōu)勢更為明顯:

async function asyncFunc() {
    const result1 = await otherAsyncFunc1();
    console.log(result1);
    const result2 = await otherAsyncFunc2();
    console.log(result2);
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc1()
    .then(result1 => {
        console.log(result1);
        return otherAsyncFunc2();
    })
    .then(result2 => {
        console.log(result2);
    });
}

并行處理多個異步函數(shù):

async function asyncFunc() {
    const [result1, result2] = await Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ]);
    console.log(result1, result2);
}

// Equivalent to:
function asyncFunc() {
    return Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ])
    .then([result1, result2] => {
        console.log(result1, result2);
    });
}

處理錯誤:

async function asyncFunc() {
    try {
        await otherAsyncFunc();
    } catch (err) {
        console.error(err);
    }
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc()
    .catch(err => {
        console.error(err);
    });
}

Async Functions若是要展開去講摄咆,可以占用很大段的篇幅凡蚜。鑒于本文是一篇介紹性文章,再次不再進(jìn)行深入吭从。

SharedArrayBuffer和Atomics

朝蜘,如果之前您沒有接觸過ArrayBuffer相關(guān)知識的話,建議您從內(nèi)存管理速成教程系列漫畫解說入門涩金,強(qiáng)推:
A crash course in memory management
A cartoon intro to ArrayBuffers and SharedArrayBuffers

Avoiding race conditions in SharedArrayBuffers with Atomics


ECMAScript 2017 特性 SharedArrayBuffer 和 atomics”谱醇,由Lars T. Hansen設(shè)計暇仲。它引入了一個新的構(gòu)造函數(shù) SharedArrayBuffer 和 具有輔助函數(shù)的命名空間對象 Atomics。

在我們開始之前副渴,讓我們澄清兩個相似但截然不同的術(shù)語:并行(Parallelism) 和 并發(fā)(Concurrency) 奈附。他們存在許多定義,我使用的定義如下

  • 并行(Parallelism) (parallel 并行 vs. serial 串行):同時執(zhí)行多個任務(wù)煮剧;
  • 并發(fā)(Concurrency) (concurrent 并發(fā) vs. sequential 連續(xù)):在重疊的時間段內(nèi)(而不是一個接一個)執(zhí)行幾個任務(wù)斥滤。

JS并行的歷史

  • JavaScript 在單線程中執(zhí)行。某些任務(wù)可以異步執(zhí)行:瀏覽器通常會在單線程中運(yùn)行這些任務(wù)勉盅,然后通過回調(diào)將結(jié)果重新加入到單線程中佑颇。
  • Web workers 將任務(wù)并行引入了 JavaScript :這些是相對重量級的進(jìn)程。每個 workers 都有自己的全局環(huán)境草娜。默認(rèn)情況下挑胸,不共享任何內(nèi)容。 workers 之間的通信(或在 workers 和主線程之間的通信)發(fā)展:
    • 起初宰闰,你只能發(fā)送和接收字符串茬贵。
    • 然后,引入結(jié)構(gòu)化克乱轶 :可以發(fā)送和接收數(shù)據(jù)副本闷沥。結(jié)構(gòu)化克隆適用于大多數(shù)數(shù)據(jù)(JSON 數(shù)據(jù),TypedArray咐容,正則表達(dá)式,Blob對象蚂维,ImageData對象等)戳粒。它甚至可以正確處理對象之間的循環(huán)引用。但是虫啥,不能克隆 error 對象蔚约,function 對象和 DOM 節(jié)點(diǎn)。
    • 可在 workers 之間的轉(zhuǎn)移數(shù)據(jù):當(dāng)接收方獲得數(shù)據(jù)時涂籽,發(fā)送方失去訪問權(quán)限苹祟。
  • 通過 WebGL 使用 GPU 計算(它傾向于數(shù)據(jù)并行處理)

共享數(shù)組緩沖區(qū)(Shared Array Buffers)

共享陣列緩沖區(qū)是更高并發(fā)抽象的基本構(gòu)建塊。它們允許您在多個 workers 和主線程之間共享 SharedArrayBuffer 對象的字節(jié)(該緩沖區(qū)是共享的评雌,用于訪問字節(jié)树枫,將其封裝在一個 TypedArray 中)這種共享有兩個好處:

你可以更快地在 workers 之間共享數(shù)據(jù)。
workers 之間的協(xié)調(diào)變得更簡單和更快(與 postMessage() 相比)景东。

// main.js
const worker = new Worker('worker.js');

// 要分享的buffer
const sharedBuffer = new SharedArrayBuffer( // (A)
    10 * Int32Array.BYTES_PER_ELEMENT); // 10 elements

// 使用Worker共用sharedBuffer
worker.postMessage({sharedBuffer}); // clone

// 僅限本地使用
const sharedArray = new Int32Array(sharedBuffer); // (B)

創(chuàng)建一個共享數(shù)組緩沖區(qū)(Shared Array Buffers)的方法與創(chuàng)建普通的數(shù)組緩沖區(qū)(Array Buffer)類似:通過調(diào)用構(gòu)造函數(shù)砂轻,并以字節(jié)的形式指定緩沖區(qū)的大小(行A)。你與 workers 共享的是 緩沖區(qū)(buffer) 斤吐。對于你自己的本地使用搔涝,你通常將共享數(shù)組緩沖區(qū)封裝在 TypedArray 中(行B)厨喂。

workers的實(shí)現(xiàn)如下所列。

// worker.js
self.addEventListener('message', function (event) {
    const {sharedBuffer} = event.data;
    const sharedArray = new Int32Array(sharedBuffer); // (A)
    // ···
});

sharedArrayBuffer 的 API

構(gòu)造函數(shù):

  • new SharedArrayBuffer(length)
    創(chuàng)建一個 length 字節(jié)的 buffer(緩沖區(qū))庄呈。

靜態(tài)屬性:

  • get SharedArrayBuffer[Symbol.species]
    默認(rèn)情況下返回 this蜕煌。 覆蓋以控制 slice() 的返回。

實(shí)例屬性:

  • get SharedArrayBuffer.prototype.byteLength()
    返回 buffer(緩沖區(qū)) 的字節(jié)長度诬留。

  • SharedArrayBuffer.prototype.slice(start, end)
    創(chuàng)建一個新的 this.constructor[Symbol.species] 實(shí)例幌绍,并用字節(jié)填充從(包括)開始到(不包括)結(jié)束的索引。

Atomics: 安全訪問共享數(shù)據(jù)

舉一個例子

// main.js
sharedArray[1] = 11;
sharedArray[2] = 22;

在單線程中故响,您可以重新排列這些寫入操作傀广,因為在中間沒有讀到任何內(nèi)容。 對于多線程彩届,當(dāng)你期望以特定順序執(zhí)行寫入操作時伪冰,就會遇到麻煩:

// worker.js
while (sharedArray[2] !== 22) ;
console.log(sharedArray[1]); // 0 or 11

Atomics 方法可以用來與其他 workers 進(jìn)行同步。例如樟蠕,以下兩個操作可以讓你讀取和寫入數(shù)據(jù)贮聂,并且不會被編譯器重新排列:

  • Atomics.load(ta : TypedArray, index)
  • Atomics.store(ta : TypedArray, index, value : T)

這個想法是使用常規(guī)操作讀取和寫入大多數(shù)數(shù)據(jù),而 Atomics 操作(load 寨辩,store 和其他操作)可確保讀取和寫入安全吓懈。通常,您將使用自定義同步機(jī)制靡狞,例如鎖耻警,其實(shí)現(xiàn)基于Atomics。

這是一個非常簡單的例子甸怕,它總是有效的:

// main.js
console.log('notifying...');
Atomics.store(sharedArray, 0, 123);

// worker.js
while (Atomics.load(sharedArray, 0) !== 123) ;
console.log('notified');

Atomics 的 API

Atomic 函數(shù)的主要操作數(shù)必須是 Int8Array 甘穿,Uint8Array ,Int16Array 梢杭,Uint16Array 温兼,Int32Array 或 Uint32Array 的一個實(shí)例。它必須包裹一個 SharedArrayBuffer 武契。

所有函數(shù)都以 atomically 方式進(jìn)行操作募判。存儲操作的順序是固定的并且不能由編譯器或 CPU 重新排序。

加載和存儲

  • Atomics.load(ta : TypedArray<T>, index) : T
    讀取和返回 ta[index] 上的元素咒唆,返回數(shù)組指定位置上的值届垫。
  • Atomics.store(ta : TypedArray<T>, index, value : T) : T
    在 ta[index] 上寫入 value,并且返回 value钧排。
  • Atomics.exchange(ta : TypedArray<T>, index, value : T) : T
    將 ta[index] 上的元素設(shè)置為 value 敦腔,并且返回索引 index 原先的值。
  • Atomics.compareExchange(ta : TypedArray<T>, index, expectedValue, replacementValue) : T
    如果 ta[index] 上的當(dāng)前元素為 expectedValue , 那么使用 replacementValue 替換恨溜。并且返回索引 index 原先(或者未改變)的值符衔。

簡單修改 TypeArray 元素

以下每個函數(shù)都會在給定索引處更改 TypeArray 元素:它將一個操作符應(yīng)用于元素和參數(shù)找前,并將結(jié)果寫回元素。它返回元素的原始值判族。

  • Atomics.add(ta : TypedArray<T>, index, value) : T
    執(zhí)行 ta[index] += value 并返回 ta[index] 的原始值躺盛。
  • Atomics.sub(ta : TypedArray<T>, index, value) : T
    執(zhí)行 ta[index] -= value 并返回 ta[index] 的原始值。
  • Atomics.and(ta : TypedArray<T>, index, value) : T
    執(zhí)行 ta[index] &= value 并返回 ta[index] 的原始值形帮。
  • Atomics.or(ta : TypedArray<T>, index, value) : T
    執(zhí)行 ta[index] |= value 并返回 ta[index] 的原始值槽惫。
  • Atomics.xor(ta : TypedArray<T>, index, value) : T
    執(zhí)行 ta[index] ^= value 并返回 ta[index] 的原始值。

等待和喚醒

  • Atomics.wait(ta: Int32Array, index, value, timeout=Number.POSITIVE_INFINITY) : ('not-equal' | 'ok' | 'timed-out')
    如果 ta[index] 的當(dāng)前值不是 value 辩撑,則返回 'not-equal'界斜。否則繼續(xù)等待,直到我們通過 Atomics.wake() 喚醒或直到等待超時合冀。 在前一種情況下各薇,返回 'ok'。在后一種情況下君躺,返回'timed-out'峭判。timeout 以毫秒為單位。記住此函數(shù)執(zhí)行的操作:“如果 ta[index] 為 value棕叫,那么繼續(xù)等待” 林螃。
  • Atomics.wake(ta : Int32Array, index, count)
    喚醒等待在 ta[index] 上的 count workers。

Object.values and Object.entries

Object.values() 方法返回一個給定對象自己的所有可枚舉屬性值的數(shù)組俺泣,值的順序與使用for...in循環(huán)的順序相同 ( 區(qū)別在于for-in循環(huán)枚舉原型鏈中的屬性 )疗认。

obj參數(shù)是需要待操作的對象∑鲋停可以是一個對象侮邀,或者一個數(shù)組(是一個帶有數(shù)字下標(biāo)的對象,[10,20,30] -> {0: 10,1: 20,2: 30})贝润。

const obj = { x: 'xxx', y: 1 };
Object.values(obj); // ['xxx', 1]

const obj = ['e', 's', '8']; // 相當(dāng)于 { 0: 'e', 1: 's', 2: '8' };
Object.values(obj); // ['e', 's', '8']

// 當(dāng)我們使用數(shù)字鍵值時,返回的是數(shù)字排序
// 根據(jù)鍵值排序
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.values(obj); // ['yyy', 'zzz', 'xxx']

Object.values('es8'); // ['e', 's', '8']

Object.entries 方法返回一個給定對象自身可遍歷屬性 [key, value] 的數(shù)組铝宵, 排序規(guī)則和 Object.values 一樣打掘。這個方法的聲明比較瑣碎:

const obj = { x: 'xxx', y: 1 };
Object.entries(obj); // [['x', 'xxx'], ['y', 1]]

const obj = ['e', 's', '8'];
Object.entries(obj); // [['0', 'e'], ['1', 's'], ['2', '8']]

const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10': 'xxx']]

Object.entries('es8'); // [['0', 'e'], ['1', 's'], ['2', '8']]

String padding

為 String 對象增加了 2 個函數(shù):padStart 和 padEnd。

像它們名字那樣鹏秋,這幾個函數(shù)的主要目的就是填補(bǔ)字符串的首部和尾部尊蚁,為了使得到的結(jié)果字符串的長度能達(dá)到給定的長度。你可以通過特定的字符侣夷,或者字符串横朋,或者默認(rèn)的空格填充它。下面是函數(shù)的聲明:

str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])

這些函數(shù)的第一個參數(shù)是 targetLength(目標(biāo)長度)百拓,這個是結(jié)果字符串的長度琴锭。第二個參數(shù)是可選的 padString(填充字符)晰甚,一個用于填充到源字符串的字符串。默認(rèn)值是空格决帖。

'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'

Object.getOwnPropertyDescriptors

getOwnPropertyDescriptors 方法返回指定對象所有自身屬性的描述對象厕九。屬性描述對象是直接在對象上定義的,而不是繼承于對象的原型地回。ES2017加入這個函數(shù)的主要動機(jī)在于方便將一個對象深度拷貝給另一個對象扁远,同時可以將getter/setter拷貝。聲明如下:

Object.getOwnPropertyDescriptors(obj)

obj 是待操作對象刻像。返回的描述對象鍵值有:configurable, enumerable, writable, get, set and value畅买。

const obj = { 
  get es7() { return 777; },
  get es8() { return 888; }
};
Object.getOwnPropertyDescriptor(obj);
// {
//   es7: {
//     configurable: true,
//     enumerable: true,
//     get: function es7(){}, //the getter function
//     set: undefined
//   },
//   es8: {
//     configurable: true,
//     enumerable: true,
//     get: function es8(){}, //the getter function
//     set: undefined
//   }
// }

結(jié)尾逗號

結(jié)尾逗號用代碼展示非常明了:

// 參數(shù)定義時
function foo(
    param1,
    param2,
) {}

// 函數(shù)調(diào)用時
foo(
    'abc',
    'def',
);

// 對象中
let obj = {
    first: 'Jane',
    last: 'Doe',
};

// 數(shù)組中
let arr = [
    'red',
    'green',
    'blue',
];

這個改動有什么好處呢?

  • 首先细睡,重新排列項目更簡單谷羞,因為如果最后一項更改其位置,則不必添加和刪除逗號纹冤。
  • 其次洒宝,它可以幫助版本控制系統(tǒng)跟蹤實(shí)際發(fā)生的變化。例如萌京,從:
[
    'foo'
]

修改為

[
    'foo',
    'bar'
]

導(dǎo)致線條'foo'和線條'bar'被標(biāo)記為已更改雁歌,即使唯一真正的變化是后一條線被添加。

ES9新特性(ECMAScript 2018)

ECMAScript 2018

ES9的新特性索引如下:

主要新功能:

  • 異步迭代(Domenic Denicola知残,Kevin Smith)
  • Rest/Spread 屬性(SebastianMarkb?ge)

新的正則表達(dá)式功能:

  • RegExp named capture groups(Gorkem Yakin靠瞎,Daniel Ehrenberg)
  • RegExp Unicode Property Escapes(Mathias Bynens)
  • RegExp Lookbehind Assertions(Gorkem Yakin,NozomuKatō求妹,Daniel Ehrenberg)
  • s (dotAll) flag for regular expressions(Mathias Bynens)

其他新功能:

  • Promise.prototype.finally() (Jordan Harband)
  • 模板字符串修改(Tim Disney)

異步迭代

首先來回顧一下同步迭代器:

ES6引入了同步迭代器乏盐,其工作原理如下:

  • Iterable:一個對象,表示可以通過Symbol.iterator方法進(jìn)行迭代制恍。
  • Iterator:通過調(diào)用iterable [Symbol.iterator] ()返回的對象父能。它將每個迭代元素包裝在一個對象中,并通過其next()方法一次返回一個净神。
  • IteratorResult:返回的對象next()何吝。屬性value包含一個迭代的元素,屬性done是true 后最后一個元素鹃唯。

示例:

const iterable = ['a', 'b'];
const iterator = iterable[Symbol.iterator]();
iterator.next()
// { value: 'a', done: false }
iterator.next()
// { value: 'b', done: false }
iterator.next()
// { value: undefined, done: true }

異步迭代器

先前的迭代方式是同步的爱榕,并不適用于異步數(shù)據(jù)源。例如坡慌,在以下代碼中黔酥,readLinesFromFile()無法通過同步迭代傳遞其異步數(shù)據(jù):

for (const line of readLinesFromFile(fileName)) {
    console.log(line);
}

異步迭代器和常規(guī)迭代器的工作方式非常相似,但是異步迭代器涉及promise:

async function example() {
  // 普通迭代器:
  const iterator = createNumberIterator();
  iterator.next(); // Object {value: 1, done: false}
  iterator.next(); // Object {value: 2, done: false}
  iterator.next(); // Object {value: 3, done: false}
  iterator.next(); // Object {value: undefined, done: true}

  // 異步迭代器:
  const asyncIterator = createAsyncNumberIterator();
  const p = asyncIterator.next(); // Promise
  await p;// Object {value: 1, done: false}
  await asyncIterator.next(); // Object {value: 2, done: false}
  await asyncIterator.next(); // Object {value: 3, done: false}
  await asyncIterator.next(); // Object {value: undefined, done: true}
}

異步迭代器對象的next()方法返回了一個Promise,解析后的值跟普通的迭代器類似跪者。
用法:iterator.next().then(({ value, done })=> {//{value: ‘some val’, done: false}}

const promises = [
    new Promise(resolve => resolve(1)),
    new Promise(resolve => resolve(2)),
    new Promise(resolve => resolve(3)),
];

async function test() {
    for await (const p of promises) {
        console.log(p);
    }
}
test(); //1 ,2 3

Rest/Spread 屬性

這個就是我們通常所說的rest參數(shù)和擴(kuò)展運(yùn)算符棵帽,這項特性在ES6中已經(jīng)引入,但是ES6中的作用對象僅限于數(shù)組:

restParam(1, 2, 3, 4, 5);

function restParam(p1, p2, ...p3) {
  // p1 = 1
  // p2 = 2
  // p3 = [3, 4, 5]
}

const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100

在ES9中坑夯,為對象提供了像數(shù)組一樣的rest參數(shù)和擴(kuò)展運(yùn)算符:

const obj = {
  a: 1,
  b: 2,
  c: 3
}
const { a, ...param } = obj;
  console.log(a)     //1
  console.log(param) //{b: 2, c: 3}

function foo({a, ...param}) {
  console.log(a);    //1
  console.log(param) //{b: 2, c: 3}
}

正則表達(dá)式命名捕獲組

編號的捕獲組

//正則表達(dá)式命名捕獲組
const RE_DATE = /([0-9]{4})-([0-9]{2})-([0-9]{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31

通過數(shù)字引用捕獲組有幾個缺點(diǎn):

  • 找到捕獲組的數(shù)量是一件麻煩事:必須使用括號岖寞。
  • 如果要了解組的用途,則需要查看正則表達(dá)式柜蜈。
  • 如果更改捕獲組的順序仗谆,則還必須更改匹配代碼。

命名的捕獲組

ES9中可以通過名稱來識別捕獲組:(?<year>[0-9]{4})

在這里淑履,我們用名稱標(biāo)記了前一個捕獲組year隶垮。該名稱必須是合法的JavaScript標(biāo)識符(認(rèn)為變量名稱或?qū)傩悦Q)。匹配后秘噪,您可以通過訪問捕獲的字符串matchObj.groups.year來訪問狸吞。

讓我們重寫前面的代碼:

const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

// 使用解構(gòu)語法更為簡便
const {groups: {day, year}} = RE_DATE.exec('1999-12-31');
console.log(year); // 1999
console.log(day); // 31

可以發(fā)現(xiàn),命名捕獲組有以下優(yōu)點(diǎn):

  • 找到捕獲組的“ID”更容易指煎。
  • 匹配代碼變?yōu)樽悦枋鲂缘奶F驗椴东@組的ID描述了正在捕獲的內(nèi)容。
  • 如果更改捕獲組的順序至壤,則無需更改匹配代碼威始。
  • 捕獲組的名稱也使正則表達(dá)式更容易理解,因為您可以直接看到每個組的用途像街。

正則表達(dá)式 Unicode 轉(zhuǎn)義

該特性允許您使用\p{}通過提及大括號內(nèi)的Unicode字符屬性來匹配字符,在正則表達(dá)式中使用標(biāo)記 u (unicode) 設(shè)置黎棠。

/^\p{White_Space}+$/u.test('\t \n\r')
// true
/^\p{Script=Greek}+$/u.test('μετ?')
// true

新方法匹配中文字符

由于在Unicode里面,中文字符對應(yīng)的Unicode Script是Han镰绎,于是我們就可以用這個reg來匹配中文:

/\p{Script=Han}/u

這樣我們就可以不用記憶繁瑣又不好記的/[\u4e00-\u9fa5]/了脓斩,況且這個表達(dá)式已經(jīng)有些年頭了,說實(shí)話畴栖,后來又新增的屬性為Han的字符并不在這個范圍內(nèi)随静,因此這個有年頭reg并不一定好使。

我隨便從網(wǎng)上找了一個Unicode8.0添加的中文字符“??”,我測了一下兩種reg的兼容性:

oldReg=/[\u4e00-\u9fa5]/
newReg=/\p{Script=Han}/u

oldReg.test('abc')
// false
newReg.test('abc')
// false

oldReg.test('地平線')
// true
newReg.test('地平線')
// true

oldReg.test('??')
// false
newReg.test('??')
// true

http://www.unicode.org/charts/PDF/U4E00.pdf

可以參考一下這個PDF吗讶,是Unicode的漢字全集挪挤,從524頁9FA6至526頁(最后一頁)用舊匹配方式都無法生效。

Unicode

一些對于Unicode的科普

  • Name:唯一名稱关翎,由大寫字母,數(shù)字鸠信,連字符和空格組成纵寝。例如:

    • A: Name = LATIN CAPITAL LETTER A
    • ??: Name = GRINNING FACE
  • General_Category:對字符進(jìn)行分類。例如:

    • X: General_Category = Lowercase_Letter
    • $: General_Category = Currency_Symbol
  • White_Space:用于標(biāo)記不可見的間距字符,例如空格爽茴,制表符和換行符葬凳。例如:

    • \ T: White_Space = True
    • π: White_Space = False
  • Age:引入字符的Unicode標(biāo)準(zhǔn)版本。例如:歐元符號€在Unicode標(biāo)準(zhǔn)的2.1版中添加室奏。

    • €: Age = 2.1
  • Script:是一個或多個書寫系統(tǒng)使用的字符集合火焰。

    • 有些腳本支持多種寫入系統(tǒng)。例如胧沫,拉丁文腳本支持英語昌简,法語,德語绒怨,拉丁語等書寫系統(tǒng)纯赎。
    • 某些語言可以用多個腳本支持的多個備用寫入系統(tǒng)編寫。例如南蹂,土耳其語在20世紀(jì)初轉(zhuǎn)換為拉丁文字之前使用了阿拉伯文字犬金。
    • 例子:
      • α: Script = Greek
      • Д: Script = Cyrillic

正則表達(dá)式的Unicode屬性轉(zhuǎn)義

  • 匹配其屬性prop具有值的所有字符value:

    \p{prop=value}

  • 匹配所有沒有屬性prop值的字符value:

    \P{prop=value}

  • 匹配二進(jìn)制屬性bin_prop為True的所有字符:

    \p{bin_prop}

  • 匹配二進(jìn)制屬性bin_prop為False的所有字符:

    \P{bin_prop}

  • 匹配空格:

/^\p{White_Space}+$/u.test('\t \n\r')
//true

匹配字母:

/^\p{Letter}+$/u.test('πüé')
//true

匹配希臘字母:

/^\p{Script=Greek}+$/u.test('μετ?')
//true

匹配拉丁字母:

/^\p{Script=Latin}+$/u.test('Grü?e')
//true

正則表達(dá)式反向斷言

先來看下正則表達(dá)式先行斷言是什么:

如獲取貨幣的符號

const noReLookahead = /\D(\d+)/,
      reLookahead = /\D(?=\d+)/,
      match1 = noReLookahead.exec('$123.45'),
      match2 = reLookahead.exec('$123.45');
console.log(match1[0]); // $123   
console.log(match2[0]); // $

在ES9中可以允許反向斷言:

const reLookahead = /(?<=\D)[\d\.]+/;
      match = reLookahead.exec('$123.45');
console.log(match[0]); // 123.45

使用?<=進(jìn)行反向斷言,可以使用反向斷言獲取貨幣的價格六剥,而忽略貨幣符號晚顷。

正則表達(dá)式dotAll模式

正則表達(dá)式中點(diǎn).匹配除回車外的任何單字符,標(biāo)記s改變這種行為疗疟,允許行終止符的出現(xiàn)该默,例如:

/hello.world/.test('hello\nworld');  // false
/hello.world/s.test('hello\nworld'); // true

Promise.prototype.finally()

這個基本沒什么好講的,看名字就能看懂了秃嗜。其用法如下:

promise
  .then(result => {···})
  .catch(error => {···})
  .finally(() => {···});

finally的回調(diào)總會被執(zhí)行权均。

模板字符串修改

ES2018 移除對 ECMAScript 在帶標(biāo)簽的模版字符串中轉(zhuǎn)義序列的語法限制。
之前锅锨,\u開始一個 unicode 轉(zhuǎn)義叽赊,\x開始一個十六進(jìn)制轉(zhuǎn)義,\后跟一個數(shù)字開始一個八進(jìn)制轉(zhuǎn)義必搞。這使得創(chuàng)建特定的字符串變得不可能必指,例如Windows文件路徑 C:\uuu\xxx\111。

要取消轉(zhuǎn)義序列的語法限制恕洲,可在模板字符串之前使用標(biāo)記函數(shù)String.raw:

`\u{54}`
// "T"
String.raw`\u{54}`
// "\u{54}"

尾聲

ECMAScript的演化不會停止塔橡,但是我們完全沒必要害怕。除了ES6這個史無前例的版本帶來了海量的信息和知識點(diǎn)以外霜第,之后每年一發(fā)的版本都僅僅帶有少量的增量更新葛家,一年更新的東西花半個小時就能搞懂了,完全沒必要畏懼泌类。

Stay hungry. Stay foolish.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末癞谒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弹砚,老刑警劉巖双仍,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異桌吃,居然都是意外死亡朱沃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門茅诱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逗物,“玉大人,你說我怎么就攤上這事让簿【床欤” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵尔当,是天一觀的道長莲祸。 經(jīng)常有香客問我,道長椭迎,這世上最難降的妖魔是什么锐帜? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮畜号,結(jié)果婚禮上缴阎,老公的妹妹穿的比我還像新娘。我一直安慰自己简软,他們只是感情好蛮拔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痹升,像睡著了一般建炫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疼蛾,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天肛跌,我揣著相機(jī)與錄音,去河邊找鬼察郁。 笑死衍慎,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的皮钠。 我是一名探鬼主播稳捆,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼麦轰!你這毒婦竟也來了眷柔?” 一聲冷哼從身側(cè)響起期虾,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驯嘱,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喳坠,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鞠评,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了壕鹉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剃幌。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖晾浴,靈堂內(nèi)的尸體忽然破棺而出负乡,到底是詐尸還是另有隱情,我是刑警寧澤脊凰,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布抖棘,位于F島的核電站,受9級特大地震影響狸涌,放射性物質(zhì)發(fā)生泄漏切省。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一帕胆、第九天 我趴在偏房一處隱蔽的房頂上張望朝捆。 院中可真熱鬧,春花似錦懒豹、人聲如沸芙盘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽儒老。三九已至,卻和暖如春豹储,著一層夾襖步出監(jiān)牢的瞬間贷盲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工剥扣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巩剖,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓钠怯,卻偏偏與公主長得像佳魔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子晦炊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容