導(dǎo)言:ECMAScript的演化不會停止榕吼,但是我們完全沒必要害怕饿序。除了ES6這個史無前例的版本帶來了海量的信息和知識點(diǎn)以外,之后每年一發(fā)的版本都僅僅帶有少量的增量更新羹蚣,一年更新的東西花半個小時就能搞懂了原探,完全沒必要畏懼。本文將帶您花大約一個小時左右的時間,迅速過一遍ES7,ES8,ES9的新特性咽弦。
想追求更好的閱讀體驗徒蟆,請移步原文地址
題記:本文提供了一個在線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)排抬。
- stage-0 - Strawman: just an idea, possible Babel plugin.
任何討論懂从、想法、改變或者還沒加到提案的特性都在這個階段蹲蒲。只有TC39成員可以提交番甩。
當(dāng)前的stage 0列表可以查看這里 --> Stage 0 Proposals
-
stage-1 - Proposal: this is worth working on.
什么是 Proposal?一份新特性的正式建議文檔届搁。提案必須指明此建議的潛在問題缘薛,例如與其他特性之間的關(guān)聯(lián)窍育,實(shí)現(xiàn)難點(diǎn)等。
-
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)
- stage-3 - Candidate: complete spec and initial browser implementations.
候選階段仪或,獲得具體實(shí)現(xiàn)和用戶的反饋。此后追迟,只有在實(shí)現(xiàn)和使用過程中出現(xiàn)了重大問題才會修改溶其。至少要在一個瀏覽器中實(shí)現(xiàn)骚腥,提供polyfill或者babel插件敦间。
- 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)
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)
在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)
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的科普
-
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.