原本想稍微整理一下 ES 新特性搭幻,沒想到花了相當(dāng)多的時間,本文也巨長逞盆,依然推薦使用 簡悅 生成目錄檀蹋。
原文竟然由于過長無法發(fā)布,第一次知道簡書還有文章字數(shù)限制≡坡現(xiàn)在只能拆成兩篇發(fā)布俯逾。
本系列文章
ES6~ES11 特性介紹之 ES6 篇
ES6~ES11 特性介紹之 ES7~ES11 篇
特性列表
ES7(ES 2016) 特性
2016 年 6 月推出的 ES2016
規(guī)范也就是 ES7 只做了微量的更新,添加了兩個特性:
- Array.prototype.includes
- 指數(shù)操作符
#01 Array.prototype.includes()
在上文 ES6 部分的 6.3.3 includes(), startsWith(), endsWith(), repeat()
一節(jié)中舅逸,我們介紹了字符串的 includes
方法來判斷是否包含指定的「參數(shù)字符串」桌肴。ES2016
則繼續(xù)補充了數(shù)組的 includes
方法,用來判斷數(shù)組中是否包含指定的值:
const arr = [1, 2, 3];
console.log(arr.includes(1)); // true
console.log(arr.includes(4)); // false
includes
方法可以傳入第二個參數(shù)琉历,第二個參數(shù)表示「搜索的起始位置」坠七,默認為 0:
const arr = [1, 2, 3];
console.log(arr.includes(1)); // true
console.log(arr.includes(1, 1)); // false水醋。從 1 開始搜索,不存在元素 1
console.log(arr.includes(3, -1)); // true彪置。負數(shù)表示倒數(shù)位置拄踪,-1 表示從倒數(shù)第一個元素開始,向后查找
在 ES2016
出現(xiàn)之前拳魁,我們通常使用 indexOf
來判斷數(shù)組中是否包含某個指定值惶桐。但 indexOf
在語義上不夠明確直觀,同時 indexOf
內(nèi)部使用 ===
來判等潘懊,所以存在對 NaN
的誤判姚糊,includes
則修復(fù)了這個問題:
const arr = [NaN];
console.log(arr.indexOf(NaN)); // -1。表示不存在
console.log(arr.includes(NaN)); // true卦尊。存在
#02 指數(shù)操作符
ES2016
引入了指數(shù)操作符 **
叛拷,用來更為方便的進行指數(shù)計算,與 Math.pow()
等效:
console.log(Math.pow(2, 10)); // 1024岂却。計算 2 的十次方
console.log(2 ** 10); // 1024忿薇。計算 2 的十次方
ES8(ES 2017) 特性
#01 async/await
在 ES6 部分的 08 Generator 函數(shù)
一節(jié)介紹了 Generator 函數(shù),其中 8.5 在異步編程上的應(yīng)用
小節(jié)則是介紹了 Generator 自動調(diào)度器躏哩。
而所謂的 async/await
其實就是實現(xiàn)一個自動調(diào)度的 Generator 函數(shù)的語法糖署浩。
async
其實就是 Generator 函數(shù)聲明語句的語法糖,只是 async
函數(shù)多了一個限制扫尺,即返回值為 Promise
對象筋栋,即使在編碼時返回是各種各樣類型的值,但最后都會被封裝成一個 Promise
對象正驻。
而 await
相當(dāng)于 yield
弊攘。之前提及過使用第三方自動調(diào)度器則 yield
后面需要跟 Thunk 函數(shù)或 Promise 對象。而 await
后面則是統(tǒng)一跟 Promise 對象姑曙,如果不是 await
也會自動將后面表達式值轉(zhuǎn)為 Promise
對象襟交。
另一方面 async/await
在語義上也變得更為直觀和清晰。當(dāng)然更重要的是 async/await
內(nèi)部幫我們實現(xiàn)了 Generator 自動調(diào)度器伤靠。所以通過 async/await
我們將非常容易的實現(xiàn)一個自動調(diào)度的 Generator 函數(shù):
async function asyncByAsync() {
const result1 = await req("/users"); // req 需要返回 Promise 對象捣域,或者 req 也是一個 async
const result2 = await req("/admin"); // req 需要返回 Promise 對象,或者 req 也是一個 async
console.log(result1);
console.log(result2);
}
#02 對象擴展
2.1 Object.values 和 Object. entries
和 Object.keys
配套宴合,ES2017
引入了 Object.values
獲取對象的所有值焕梅,以及 Object. entries
來獲取對象的所有鍵值對:
const obj = {a: 1, b: 2, c: 3};
for (const value of Object.values(obj)) {
console.log(value); // 1、2卦洽、3
}
for (const [key, value] of Object.entries(obj)) {
console.log(key, value); // a 1贞言、b 2、c 3
}
2.2 Object.getOwnPropertyDescriptor
ES2017
之前已經(jīng)存在 Object.getOwnPropertyDescriptor()
方法阀蒂,用來獲取對象中某個屬性的描述對象蜗字。與此相對打肝,ES2017
引入了 Object.getOwnPropertyDescriptors()
方法,用來獲取對象中所有自身屬性(非繼承屬性)的描述對象:
貼心小提示:兩個方法名稱非常像挪捕,不過第一個沒有 s,第二個有 s
const obj = {a: 1, b: 2, c: 3};
console.log(Object.getOwnPropertyDescriptors(obj));
// a: {value: 1, writable: true, enumerable: true, configurable: true}
// b: {value: 2, writable: true, enumerable: true, configurable: true}
// c: {value: 3, writable: true, enumerable: true, configurable: true}
#03 SharedArrayBuffer 和 Atomics
JavaScript 是單線程的争便,但 Web Worker
則是多線程的级零,Web Worker
多線程之間通過 postMessage
、onmessage
等方式進行通信傳遞消息滞乙。但當(dāng)數(shù)據(jù)量比較大時奏纪,直接傳遞數(shù)據(jù)效率較低。因此 ES2017
引入了 SharedArrayBuffer
來實現(xiàn)共享內(nèi)存:
// 新建 1KB 共享內(nèi)存
const sharedBuffer = new SharedArrayBuffer(1024);
// 主線程將共享內(nèi)存的地址發(fā)送出去
w.postMessage(sharedBuffer);
// sharedBuffer 不能直接讀寫
// 只能通過視圖(TypedArray視圖和DataView視圖)來讀寫
// 視圖的作用是以指定格式解讀二進制數(shù)據(jù)斩启。
// 在共享內(nèi)存上建立視圖序调,方便讀寫
const sharedArray = new Int32Array(sharedBuffer);
// Worker 線程
onmessage = function (ev) {
// 主線程共享的數(shù)據(jù)
const sharedBuffer = ev.data;
// 在共享內(nèi)存上建立視圖,供寫入數(shù)據(jù)
const sharedArray = new Int32Array(sharedBuffer);
// ...
};
引入共享內(nèi)存就必然設(shè)計到多線程的競爭讀寫兔簇,那么最好是能夠封裝一些必要的「原子性操作」以供線程讀寫发绢。Atomics 對象
并是提供「原子性操作」的角色。
值得注意的是提供了「原子性操作」并不意味著不再存在并發(fā)的資源競爭問題垄琐,有興趣的可以查閱LevelDB 中的跳表實現(xiàn) 中的 「并發(fā)處理」部分边酒。
Atomics 對象
提供了 store()
、load()
狸窘、exchange()
等等方法墩朦。
#04 字符串?dāng)U展
ES2017
引入了兩個字符串補全函數(shù) padStart()
和padEnd()
。如果某個字符串不夠指定長度翻擒,那么這兩個函數(shù)可以用指定字「填補」直至達到指定長度氓涣。兩個函數(shù)分別對應(yīng)在字符串的頭部和尾部補全字符串:
// 第一個參數(shù)表示最大長度為 5,不夠則一直用 'ab' 填補在頭部
'x'.padStart(5, 'ab'); // 'ababx'
// 因為第一個參數(shù)指定最大長度為 4
// 第二次用 'ab' 填補時由于達到了 4陋气,所以只用 ab 中的 a 填補
'x'.padStart(4, 'ab'); // "abax"
'x'.padEnd(5, 'ab'); // 'xabab'
// 如果指定長度小于字符串現(xiàn)有長度
// 則不做處理劳吠,返回原字符串
'xxxx'.padStart(3, 'ab'); // 'xxxx'
// 省略第二個參數(shù)
'x'.padStart(5); // ' x' 省略第二個參數(shù),則默認使用空格 ' ' 填充
'x'.padEnd(5); // 'x '
#05 函數(shù)擴展
ES2017
規(guī)定函數(shù)的參數(shù)列表的結(jié)尾可以為逗號
:
function Dog(
name,
age,
sex, // ES2017 之前非法恩伺,ES2017 后合法
) { /* 函數(shù)體 */ }
和對象最后屬性結(jié)尾可以為逗號
的理由一樣赴背,如果沒有該逗號,添加一個參數(shù)就需要修改兩行(一行需要加個逗號晶渠,一行加屬性)凰荚,這在 git
多人協(xié)作時會干擾對本次修改的查看。
ES9(ES 2018) 特性
#01 異步迭代器
在上文 ES6 部分的 #11 Iterator 迭代器
一節(jié)中褒脯,我們已經(jīng)介紹了 Iterator
迭代器的基本概念和用法:
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator](); // 獲取迭代器
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: true}
但是上述代碼表達的迭代器是一種針對「同步數(shù)據(jù)」的「同步迭代」便瑟,無法實現(xiàn)對「異步數(shù)據(jù)」的迭代,而 ES2018
引入了對「異步迭代」的支持
重溫一下同步迭代器的三個概念:
-
Iterable: 一個數(shù)據(jù)結(jié)構(gòu)只要部署了
Symbol.iterator 屬性
番川,我們就可以稱之為Iterable
即可迭代的到涂。Symbol.iterator 屬性
為一個函數(shù)脊框,該函數(shù)應(yīng)該返回本數(shù)據(jù)結(jié)構(gòu)的迭代器Iterator 對象
。 -
Iterator:通過
Symbol.iterator
函數(shù)返回的對象即為用來訪問數(shù)據(jù)結(jié)構(gòu)的Iterator 對象
践啄。該對象通常一開始指向數(shù)據(jù)結(jié)構(gòu)的起始地址浇雹,同時具有next()
函數(shù),調(diào)用next()
函數(shù)指向第一個元素屿讽,再次調(diào)用next()
函數(shù)指向第二個元素.... 重復(fù)并可迭代數(shù)據(jù)結(jié)構(gòu)中所有元素昭灵。 -
IteratorResult:
Iterator 對象
每一次調(diào)用next()
訪問的元素會被包裝返回,返回值為一個對象伐谈,其中包含value
屬性表示值烂完、done
屬性表示是否已經(jīng)迭代完成。
異步迭代器基本和同步迭代器基本一一對應(yīng):
-
Async Iterable: 與同步迭代器的
Symbol.iterator 屬性
相對應(yīng)诵棵,異步迭代器需要部署的是Symbol.asyncIterator
屬性抠蚣。 -
Iterator:與同步迭代器相對應(yīng),只不過其中的
next()
函數(shù)返回的是對 IteratorResult 結(jié)果的Promise
封裝履澳。 -
IteratorResult:與同步迭代器相對應(yīng)嘶窄。只是不再被直接返回,而是封裝成了一個
Promise
對象奇昙。
異步迭代器的使用:
// 自定義一個實現(xiàn)了 `Symbol.asyncIterator ` 屬性的 AsyncIterable 數(shù)據(jù)結(jié)構(gòu)
const asyncIterable = new AsyncIterable(['a', 'b']);
// 得到異步迭代器對象
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
// next() 返回的是一個 promise 對象
asyncIterator.next()
.then(iterResult1 => { // 通過 .then 處理結(jié)果
// { value: 'a', done: false } // 返回的 IteratorResult 結(jié)果
console.log(iterResult1);
// 迭代下一個異步數(shù)據(jù)
return asyncIterator.next();
})
.then(iterResult2 => {
// { value: 'b', done: false }
console.log(iterResult2);
return asyncIterator.next();
})
.then(iterResult3 => {
// { value: undefined, done: true }
console.log(iterResult3);
});
當(dāng)然還可以結(jié)合 async/await
簡化實現(xiàn):
async function f() {
const asyncIterable = new AsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
// { value: 'a', done: false }
console.log(await asyncIterator.next());
// { value: 'b', done: false }
console.log(await asyncIterator.next());
// { value: undefined, done: true }
console.log(await asyncIterator.next());
}
之前也提及過同步迭代器可以使用 for...of
語句進行循環(huán)护侮,而對于異步迭代器則可以使用 for await...of
語句:
async function f() {
for await (const x of createAsyncIterable([1, 2])) {
console.log(x);
}
}
#02 Promise.prototype.finally()
ES2018
在 Promise
方面也做了一些補充,添加了 finally()
方法储耐,表示無論 Promise
實例最終成功或失敗都會執(zhí)行的方法:
const promise = new Promise(function(resolve, reject) {
setTimeout(() => {
const one = '1';
reject(one);
}, 1000);
});
promise
.then(() => console.log('success'))
.catch(() => console.log('fail'))
.finally(() => console.log('finally'))
finally()
函數(shù)不接受參數(shù)羊初,finally()
內(nèi)部通常不知道 promise
實例的執(zhí)行結(jié)果,所以通常在 finally()
方法內(nèi)執(zhí)行的是與 promise
狀態(tài)無關(guān)的操作什湘。
#03 對象的 Rest/Spread
在 ES6 部分的 2.1.3 rest 操作符 ...
節(jié) 和 6.5.1 spread 擴展運算符與數(shù)組
節(jié)分別簡單介紹了 rest 運算符
和 spread 運算符
长赞,如下所示:
// rest 運算符: 將元素組織成數(shù)組
const [a, ...b] = [1, 2, 3];
console.log(b); // 輸出 [2, 3]
(function(...arr) {
console.log(arr); // 輸出 [1, 2, 3]
}(1, 2, 3));
// spread 運算符:將數(shù)組擴展為元素
const arr = [1, 2, 3];
console.log(...arr); // 輸出 1 2 3
但是之前 rest/spread 運算符
只能作用于數(shù)組,ES2018
可以實現(xiàn)將其作用于對象:
// 1. rest 運算符: 將元素組織成對象
const obj = {a: 1, b: 2, c: 3};
const {a, ...rest} = obj;
console.log(rest); // 輸出 {b: 2, c: 3}
(function({a, ...obj}) {
console.log(obj); // 輸出 {b: 2, c: 3}
}({a: 1, b: 2, c: 3}));
// 2. spread 運算符:將對象擴展為元素
const obj = {a: 1, b: 2, c: 3};
const newObj ={...obj, d: 4};
console.log(newObj); // 輸出 {a: 1, b: 2, c: 3, d: 4}
// 可以用來合并對象
const obj1 = {a: 1, b:2};
const obj2 = {c: 3, d:4};
const mergedObj = {...obj1, ...obj2};
console.log(mergedObj); // 輸出 {a: 1, b: 2, c: 3, d: 4}
#04 正則表達式的擴展
4.1 正則表達式 dotAll 模式
正則表達式中闽撤,.
符號代表匹配任意單個字符得哆。但特殊字符 \n
、\r
等「行終止符」無法被 .
匹配哟旗。
ES2018
引入了 s
修飾符實現(xiàn) dotAll 模式
(.
真正匹配包含行終止符在內(nèi)的任意單個字符):
const reg = /dog.age/;
reg.test('dog\nage'); // false贩据。dot . 無法匹配 /n
const reg_dotall = /dog.age/s; // 通過 s 修飾符實現(xiàn) dotAll 模式
reg_dotall.test('dog\nage'); // true
4.2 正則表達式后行斷言
在 ES2018
之前,JavaScript 正則表達式支持「先行斷言」和「先行否定斷言」闸餐。
-
先行斷言
x(?=pattern)
緊接x
之后的字符應(yīng)該匹配pattern
饱亮,匹配上后返回的結(jié)果不包含匹配pattern
的字符。由于pattern
不消耗字符舍沙,所以這類斷言也被視為「零寬斷言」近上。
const reg = /aa(?=bb)/; // aa 后面緊跟 bb
reg.exec('aabb'); // 輸出 aa。aabb 符合上述模式拂铡,但匹配結(jié)果不會包含 bb 字符壹无。
-
先行否定斷言
x(?!pattern)
緊接x
之后的字符應(yīng)該不匹配pattern
:
const reg = /aa(?!bb)/; // aa 后面不能緊跟 bb
reg.exec('aabb'); // null
reg.exec('aab'); // 輸出 aa葱绒。aab 滿足上述模式,但注意匹配結(jié)果不會包含括號 () 里的內(nèi)容
ES2018
補充了與先行相對應(yīng)的「后行斷言」和「后行否定斷言」:
-
后行斷言
(?<=pattern)x
緊接x
之前的字符應(yīng)該匹配pattern
:
const reg = /(?<=aa)bb/; // bb 前面緊接著 aa
reg.exec('aabb'); // 輸出 bb斗锭。aabb 滿足模式地淀,但是匹配結(jié)果不包含括號內(nèi)的內(nèi)容即 aa
-
后行否定斷言
(?<!pattern)x
緊接x
之前的字符應(yīng)該不匹配pattern
:
const reg = /(?<!aa)bb/; // bb 前面不能緊接著 aa
reg.exec('aabb'); // null
reg.exec('abb'); // 輸出 bb。bb 前面沒有緊接著 aa拒迅,滿足模式骚秦。但輸出結(jié)果不會包含 a
4.3 正則表達式 Unicode 屬性類
ES2018
引入了 \p{...}
和 \P{...}
來實現(xiàn)對 Unicode 屬性
的指定。例如指定查找范圍為「希臘符號」璧微,以前只能限定字符區(qū)間,現(xiàn)在可以直接指定屬性:
// \p 匹配滿足條件的數(shù)據(jù)
const reg1 = /\p{Script=Greek}/u; // 指定 Script 為 Greek硬梁,即只匹配「希臘字符」前硫。
reg1.test('π') // true
// \P 與 \p 相反,表示匹配不滿足條件的數(shù)據(jù)
const reg2 = /\P{Script=Greek}/u; // 指定 Script 為 Greek荧止,但 \P 表示匹配「非希臘字符」屹电。
reg2.test('π') // false
4.4 正則表達式具名組匹配
在 ES2018
之前,我們可以通過 ()
來實現(xiàn)分組:
const reg = /(\d{4})-(\d{2})-(\d{2})/;
const arr = reg.exec('2020-12-31');
const year = arr[1]; // 2020
const month = arr[2]; // 12
const day = arr[3]; // 31
上面的分組通過數(shù)組來對應(yīng)跃巡,每個括號對應(yīng)一個數(shù)組元素危号,通過下標來訪問。這種方式在語義上不夠清晰素邪,在操作時也不夠直觀外莲。所以 ES2018
引入了 ?<name>
實現(xiàn)給組進行命名:
// 在 \d{4} 前面添加 ?<year>,給該組命名為 year
// ?<month> 和 ?<day> 同理
const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const obj = reg.exec('2020-12-31');
// 直接通過 year 名稱獲取組匹配結(jié)果
const year = obj.groups.year; // 2020
const month = obj.groups.month; // 12
const day = obj.groups.day; // 31
#05 模板字符串的轉(zhuǎn)義限制放寬
在 ES6 的 6.3.1 模板字符串
一節(jié)中介紹了模板字符串和標簽?zāi)0?/strong>兔朦,其中標簽?zāi)0迥J會將字符串轉(zhuǎn)義:
// 報錯偷线。\unicode 轉(zhuǎn)義失敗
const doc = latex`\newcommand{\unicode}{\textbf{Unicode!}}`;
上述代碼中的 \unicode
被當(dāng)作 Unicode
字符進行轉(zhuǎn)義,從而導(dǎo)致錯誤沽甥。ES2018
對此的更新是「放寬」轉(zhuǎn)義限制声邦。遇到無法轉(zhuǎn)義的字符時返回 undefined
而不是報錯,這樣可以使得程序繼續(xù)運行摆舟。同時函數(shù)可以從 6.3.1 模板字符串
一節(jié)中提及的第一個參數(shù)中的 raw
屬性拿到原始字符串亥曹。
但注意上述規(guī)則針對標簽?zāi)0?/strong>,普通模板字符串遇到無法轉(zhuǎn)義的 Unicode
字符還是會報錯:
const str = `hello \unicode`;
ES10(ES 2019) 特性
#01 字符串?dāng)U展
1.1 U+2028 和 U+2029(JSON Superset)
在 ES2019
之前恨诱,JavaScript 的字符串中是不能直接包含一些特殊 Unicode 字符媳瞪,例如 U+000D回車
、U+000A 換行
以及 U+2028 行分隔符
和 U+2029 段分隔符
等胡野。
但是由于 JSON 格式規(guī)范允許字符串里直接包含 U+2028 行分隔符
和 U+2029 段分隔符
(非轉(zhuǎn)義形式)材失,這樣將導(dǎo)致 JavaScript 在解析包含這兩種特殊字符的 JSON 串時出錯。
于是 ES2019
添加了字符串對這兩個字符的支持硫豆,可以在字符串中使用這兩種字符:
const str1 = eval("'\u2029'"); // 不報錯龙巨。解析轉(zhuǎn)義字符 \u2029
1.2 trimStart 和 trimEnd
ES2019
添加了 trimStart
和 trimEnd
這兩個新方法笼呆,分別用來去除字符串的首部和尾部空白字符:
const hiMessage = ` Hello Word! `;
console.log(hiMessage.trimStart()); // 'Hello Word! '
console.log(hiMessage.trimEnd()); // ' Hello Word!'
#02 數(shù)組擴展
ES2019
在數(shù)組類型上添加了 flat()
和 flatMap()
這兩個新方法。
- flat()
flat
函數(shù)用來將嵌套數(shù)組(多層次數(shù)組)「拉平」:
// [1, 2, 3, 4];
[1, 2, [3, 4]].flat();
// [1, 2, 3, 4, 5] 參數(shù)為層次設(shè)置旨别,2 表示作用于內(nèi)嵌 2 層
[1, 2, [3, [4, 5]]].flat(2);
// [1, 2, 3, 4, 5]诗赌,Infinity 表示無論多少層,拉平所有層次
[1, 2, [3, [4, [6]]]].flat(Infinity);
-
flatMap()
flatMap
函數(shù)對數(shù)組每個元素進行一些「處理」秸弛,然后處理返回的是一個數(shù)組铭若,那么數(shù)組會被flat
拉平,上述的「處理」由回調(diào)函數(shù)給出:
// 先對每個元素進行 ele * 2 递览,并返回數(shù)組叼屠,即 [2], [4], [6], [8]
// 再對返回值應(yīng)用 flat 函數(shù)拉平
// [[2], [4], [6], [8]].flat(),最終得到 [2, 4, 6, 8]
[1, 2, 3, 4].flatMap(ele => [ele * 2]); // [2, 4, 6, 8]
// 注意 flatMap 中 flat 只會拉平一層
[1, 2, 3, 4].flatMap(ele => [[ele * 2]]); // [[2], [4], [6], [8]]
#03 對象擴展
ES2019
在對象 Object
上添加了 fromEntries
方法绞铃,該方法相當(dāng)于 Object.entries
方法的逆過程镜雨,用來將一個 key-value
「鍵值對列表」轉(zhuǎn)換成「對象」。
Object.fromEntries(iterable)
所接受的參數(shù)是像 Array
儿捧、Map
那樣實現(xiàn)了 iterable
迭代協(xié)議(且迭代的每個元素包含 key\value
兩個數(shù)值)的數(shù)據(jù)結(jié)構(gòu):
// Array
const dog = [['name', 'wangwang'], ['age', 5]];
const obj1 = Object.fromEntries(dog);
console.log(obj1); // {name: "wangwang", age: 5}
// Map
const cat = new Map();
cat.set('name', 'miaomiao');
cat.set('age', 2);
const obj2 = Object.fromEntries(cat);
console.log(obj2); // {name: "miaomiao", age: 2}
#04 Symbol 的擴展
在 ES6 部分的 6.1 節(jié)介紹了 Symbol
荚坞,其中知曉傳入的參數(shù)相當(dāng)于「描述」:
let s1 = Symbol("dog"); // dog 為描述
在 ES2019
之前,獲取一個 Symbol
值的描述需要通過 String 方法
或 toString 方法
:
console.log(String(s1)); // "Symbol(dog)"
console.log(s1.toString()); // "Symbol(dog)"
ES2019
補充了屬性 description
菲盾,用來直接訪問「描述」:
console.log(s1.description); // 直接獲取到 dog
#05 函數(shù)的擴展
ES2019
對函數(shù)的 toString()
方法進行了擴展颓影,以前這個方法只會輸出函數(shù)代碼,但會省略注釋和空格懒鉴。ES2019
的 toString()
則會保留注釋诡挂、空格等,即輸出的是原汁原味的原始代碼:
function sayHi() {
/* dog dog */
console.log('wangwang.');
}
sayHi.toString(); // 將輸出和上面一樣的原始代碼
#06 JSON.stringify() 的優(yōu)化
JSON 數(shù)據(jù)需要為是 UTF-8
編碼疗我,但在 ES2019
之前 JSON.stringify()
可能輸出不符合 UTF-8
規(guī)范的字符咆畏。
原因在于 UTF-8
標準中為了表示 oxFFFF
的字符,將 0xD800
到 0xDFFF
范圍內(nèi)的編碼用來組合使用,不能單獨使用。此時將這個范圍內(nèi)的編碼傳給 JSON.stringify()
何什,函數(shù)將返回亂碼:
JSON.stringify('\uD800'); // '"?"'丈屹。 ES2019 之前
ES2019
則對這種情況進行特殊處理,如果遇到無對應(yīng)編碼結(jié)果的編碼,則直接返回轉(zhuǎn)義字符串:
JSON.stringify('\uD800'); // 返回 '"\\ud800"'。ES2019 之后
#07 無參 catch
ES2019
以前,catch
會帶有參數(shù)魏颓,而現(xiàn)在可以不帶參數(shù):
// ES2019 之前
try {
...
} catch(error) {
...
}
// ES2019 之后
try {
...
} catch {
...
}
#08 Array.prototype.sort() 穩(wěn)定
ES2019
之前數(shù)組的 sort()
方法是否穩(wěn)定是不明確的,任由瀏覽器自己實現(xiàn)吱晒。ES2019
開始明確規(guī)定排序算法必須是穩(wěn)定的甸饱。
ES11(ES 2020) 特性
#01 模塊化擴展
1.1 動態(tài)導(dǎo)入 import()
在上文 ES6 部分的 04 模塊 Module
節(jié)中介紹了模塊相關(guān)的知識,其中模塊導(dǎo)入使用 import
命令實現(xiàn),但是了解到 import
命令是一種靜態(tài)導(dǎo)入叹话,所以在 ES2020
之前我們無法像 CommonJS
中的 require
那樣動態(tài)導(dǎo)入模塊偷遗。那么「條件導(dǎo)入」、「運行時確定模塊」等功能就會被限制驼壶。
ES2020
引入了 import()
操作符來實現(xiàn)動態(tài)導(dǎo)入:
const module = 'animal';
if (type === 1) {
module = 'dog';
} else if (type === 2) {
module = 'cat';
}
// 導(dǎo)入模塊的名稱可以動態(tài)計算氏豌,即運行時再確定
const utils = await import(module);
// 使用 .then 進行回調(diào)處理
import(module)
.then(module => module.sayHi())
.catch(error => console.log(error));
1.2 import.meta
ES2020
還引入了 import.meta 對象,其中包含了模塊的一些元信息热凹,例如import.meta.url
存儲了瀏覽器 URL 或文件名泵喘。
1.3 export * as module from "xxx";
在 ES6 中 4.3 導(dǎo)入導(dǎo)出混合寫法
一節(jié)中介紹了 import
和 export
混合用法,例如:
export * from 'my_module';
但是還有一種導(dǎo)入寫法沒有對應(yīng)的混合寫法:
import * as module from "xxx";
ES2020
補充了上述語句對應(yīng)的混合導(dǎo)出寫法:
export * as module from "xxx";
02 字符串?dāng)U展
2.1 matchAll
在正則表達式中般妙,如果一個字符串有多個匹配纪铺,我們可以通過循環(huán)來依次獲取:
const reg = /dog/g;
const str = 'dog1dog2dog3dog4';
const dogs = [];
let dog;
// 循環(huán)調(diào)用 exec碟渺,依次獲取匹配結(jié)果
while (dog = reg.exec(str)) {
dogs.push(dog);
}
console.log(dogs); // array[4]
ES2020
字符串引入了 matchAll
方法霹陡,可以一次性取出所有匹配,并返回一個結(jié)果的迭代器:
const reg = /dog/g;
const str = 'dog1dog2dog3dog4';
const dogs = [];
// 一次性取出所有匹配結(jié)果止状,并返回迭代器
for (const dog of str.matchAll(reg)) {
dogs.push(dog);
}
console.log(dogs); // array[4]
#03 BigInt 類型
在 JavaScript 中,數(shù)值類型 Number
被保存為** 64 位浮點數(shù)攒霹,所以計算精度和表示范圍都有一定限制怯疤。ES2020
新增了 BigInt
數(shù)據(jù)類型,這也是 JavaScript 引入的第八種基本類型**催束。
BigInt
被用來表示整數(shù)集峦,以 n
為后綴,且沒有位數(shù)限制:
// BigInt 使用 n 作為整數(shù)后綴
const a = 416576138978n; // 后綴使用 n
const b = 861387698979n;
console.log(a * b); // 358833561803815532703462n
// 普通整數(shù)精度丟失
console.log(Number(a) * Number(b)); // 3.5883356180381556e+23
BitInt
整數(shù)和普通整數(shù)不全等(類型不同):
console.log(123n === 123); // false
自然還有配套的 BigInt
對象抠刺,可以通過對應(yīng)的構(gòu)造函數(shù)創(chuàng)建 BigInt
數(shù)值塔淤,
const num = BigInt('1233243423');
BigInt
對象具備 asUintN
、parseInt
等方法:
// 1233243423n速妖。asUintN 將給定的 BigInt 轉(zhuǎn)為 0 到 2^width - 1 之間對應(yīng)的值高蜂。
BigInt.asUintN(1024, num);
// 23423423n。類似于 Number.parseInt()罕容,將一個字符串轉(zhuǎn)換成指定進制的 BigInt备恤。
//(現(xiàn)有瀏覽器不一定支持)
BigInt.parseInt('23423423', 10);
#04 Promise 擴展
4.1 Promise.allSettled()
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
Promise.allSettled(promises)
.then(results => console.log(results));
allSettled()
方法將一組 Promise
實例參數(shù)封裝成一個 Promise
實例,只有當(dāng)被封裝的所有 Promise 實例全部返回結(jié)果后(不管是成功 fulfilled
還是失敗 rejected
)才算結(jié)束锦秒。
同時所有 Promise 實例的返回結(jié)果被封裝在 results
參數(shù)中傳遞給通過 .then()
函數(shù)指定的回調(diào)函數(shù)露泊。
results
中的每個元素都對應(yīng)一個 Promise
實例的執(zhí)行結(jié)果,結(jié)果中的 status
表示執(zhí)行成功與否旅择。如果狀態(tài)為 fulfilled
惭笑,則另一個字段為 value
表示操作返回值。如果狀態(tài)為 rejected
,則另一個字段為 reason
表示錯誤信息:
Promise.allSettled(promises)
.then(results => console.log(results));
// results
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]
#05 globalThis
在 ES2020
之前沉噩,瀏覽器
捺宗、Web Worker
、Node.js
等不同環(huán)境的「頂層對象」有所不同屁擅。例如瀏覽器中可以通過 window
或 self
來獲取頂層對象偿凭,Web Worker
通過 self
獲取頂層對象,到了 Node.js
則是 global
派歌。
這種不一致性會導(dǎo)致代碼需要進行無謂的判斷弯囊,添加復(fù)雜性。所以 ES2020
引入了統(tǒng)一的 globalThis
表示頂層對象胶果。以后不管在哪個環(huán)境匾嘱,都可以通過 globalThis
拿到頂層對象。
#06 鏈判斷運算符
在平時的編程中早抠,我們經(jīng)常需要獲取深層次屬性霎烙,例如 system.user.addr.province.name
。但在獲取 name
這個屬性前我們需要一步步的判斷前面的屬性是否存在蕊连,否則并會報錯:
const name = (system && system.user && system.user.addr && system.user.addr.province && system.user.addr.province.name) || 'default';
為了簡化上述過程悬垃,ES2020
引入了「鏈判斷運算符」?.
:
const name = system?.user?.addr?.province?.name || 'default';
?.
會判斷運算符左側(cè)的對象是否存在,如果不存在即為 null
或 undefined
那么就會不再繼續(xù)而是提前返回 undefined
甘苍,存在則繼續(xù)往下運算尝蠕。
「鏈判斷運算符」還有另外幾種形式:
a?.[x]
// 等同于
a == null ? undefined : a[x]
a?.b()
// 等同于
a == null ? undefined : a.b()
a?.()
// 等同于
a == null ? undefined : a()
#06 Null 判斷運算符
在編程過程我們經(jīng)常會遇到這樣的常見,如果某個屬性不為 null
和 undefined
载庭,那么就獲取該屬性看彼,如果該屬性為 null
或 undefined
,則取一個默認值:
const name = dogName ? dogName : 'default';
可以通過 ||
來簡化:
const name = dogName || 'default';
但是 ||
的寫法存在一定的缺陷囚聚,當(dāng) dogName
為 0
或 false
的時候也會走到 default
的邏輯靖榕。
所以 ES2020
引入了 ??
運算符。只有 ??
左邊為 null
或 undefined
時才返回右邊的值:
const dogName = false;
const name = dogName ?? 'default'; // name = false;
#07 for-in 循環(huán)的規(guī)則
ES2020
對 for-in
循環(huán)進行了遍歷規(guī)則的補充顽铸,詳情可參閱proposal-for-in-order
參考資料
tc39 From GitHub
MDN Web Docs
ECMAScript 6 教程
Advanced ES6 Destructuring Techniques
The final feature set of ECMAScript 2016 (ES7)
ECMAScript 2017 (ES8): the final feature set
ECMAScript 2018: the final feature set
ES2018: asynchronous iteration
ECMAScript 2019: the final feature set
ECMAScript 2020: the final feature set
汪
汪