一覺醒來,竟發(fā)現(xiàn)自己看不懂 JS 了?
作者:林不渡
https://juejin.cn/post/6974330720994983950
前言
最近看到了一些很有趣的 ES 提案,如 Record 與 Tuple 數(shù)據(jù)類型,思路來自 RxJS 的 Observable,借鑒自函數(shù)式編程的 throw Expressions,帶來更好錯(cuò)誤處理的Error Cause等,可以認(rèn)為一旦這些提案完全進(jìn)入到 ES 新特性中,前端 er 們的工作效率又會(huì) upup,這篇文章就來介紹一下我認(rèn)為值得關(guān)注的 ES 提案。
作為前端同學(xué),即使你沒有去主動(dòng)了解過,應(yīng)該也或多或少聽說過 ECMA、ECMAScript、TC39、ES6(這個(gè)當(dāng)然了)這些詞,你可能對(duì)這些名詞代表的概念一知半解甚至是從未了解過,但這很正常,不知道這些名詞的關(guān)系并不影響你將 ES 新特性用的如臂使指。但了解一下也不虧?所以在開始正式介紹各種提案前,我們有必要先了解一下這些概念。
以下關(guān)于背景的介紹大部分來自于雪碧老師的JavaScript20 年-創(chuàng)立標(biāo)準(zhǔn)[1]一節(jié)。
ECMA(European Computer Manufacturers Association,歐洲計(jì)算機(jī)制造商協(xié)會(huì))[2],這是一個(gè)國際組織,主要負(fù)責(zé)維護(hù)各種計(jì)算機(jī)的相關(guān)標(biāo)準(zhǔn)。我們都知道 JavaScript 這門語言最早來自于網(wǎng)景(Netscape),但網(wǎng)景在和微軟(IE)的競(jìng)爭(zhēng)落得下風(fēng),為了避免最終 Web 腳本主導(dǎo)權(quán)落入微軟手中,網(wǎng)景開始尋求 ECMA 組織的幫助,來推動(dòng) JavaScript 的標(biāo)準(zhǔn)化。 在 1996 年,JavaScript 正式加入了 ECMA 大家庭,我們后面會(huì)叫它 ECMAScript(下文簡(jiǎn)稱 ES)。TC39 則是 ECMA 為 ES 專門組織的技術(shù)委員會(huì)(Technical Committee),39 這個(gè)數(shù)字則是因?yàn)?ECMA 使用數(shù)字來標(biāo)記旗下的技術(shù)委員會(huì)。TC39 的成員由各個(gè)主流瀏覽器廠商的代表構(gòu)成(因?yàn)楫吘棺詈筮€要這些人實(shí)現(xiàn)嘛)。 ECMA-262 即為 ECMA 組織維護(hù)的第 262 條標(biāo)準(zhǔn),這一標(biāo)準(zhǔn)是在不斷演進(jìn)的,如現(xiàn)在是2020 年 6 月發(fā)布的第 11 版[3]。同樣的,目前最為熟知的是2015 年發(fā)布的 ES6[4]。你還可以在TC39 的 ECMA262 官網(wǎng)[5]上看到 ES2022 的最新草案。 ECMA 還維護(hù)著許多其他方面的標(biāo)準(zhǔn),如ECMA-414[6],定義了一組 ES 規(guī)范套件的標(biāo)準(zhǔn);ECMA-404[7],定義了 JSON 數(shù)據(jù)交換的語法;甚至還有 120mm DVD 的標(biāo)準(zhǔn):ECMA267[8]。 對(duì)于一個(gè)提案從提出到最后被納入 ES 新特性,TC39 的規(guī)范中有五步要走: stage0(strawman),任何 TC39 的成員都可以提交。 stage1(proposal),進(jìn)入此階段就意味著這一提案被認(rèn)為是正式的了,需要對(duì)此提案的場(chǎng)景與 API 進(jìn)行詳盡的描述。 stage2(draft),演進(jìn)到這一階段的提案如果能最終進(jìn)入到標(biāo)準(zhǔn),那么在之后的階段都不會(huì)有太大的變化,因?yàn)槔碚撋现唤邮茉隽啃薷摹?/section> state3(candidate),這一階段的提案只有在遇到了重大問題才會(huì)修改,規(guī)范文檔需要被全面的完成。 state4(finished),這一階段的提案將會(huì)被納入到 ES 每年發(fā)布的規(guī)范之中。 有興趣的同學(xué)可以閱讀 The TC39 process for ECMAScript features[9] 了解更多。
Record & Tuple(stage2)
proposal-record-tuple[10] 這一提案為 JavaScript 新增了兩種數(shù)據(jù)結(jié)構(gòu):Record(類似于對(duì)象) 和 Tuple(類似于數(shù)組),它們的共同點(diǎn)是都是不可變的(Immutable),同時(shí)成員只能是原始類型以及同樣不可變的 Record 和 Tuple。正因?yàn)樗鼈兊某蓡T不能包含引用類型,所以它們是 按值比較 的,成員完全一致的 Record 和 Tuple 如果進(jìn)行比較,會(huì)被認(rèn)為是相同的('==='會(huì)返回 true)。
你可能會(huì)想到社區(qū)其實(shí)對(duì)于數(shù)據(jù)不可變已經(jīng)有不少方案了,如 ImmutableJS 與 Immer。而數(shù)據(jù)不可變同樣是 React 中的重要概念。
使用示例:
// Record
const proposal = #{
id: 1234,
title: "Record & Tuple proposal",
contents: `...`,
keywords: #["ecma", "tc39", "proposal", "record", "tuple"],
};
// Tuple
const measures = #[42, 12, 67, "measure error: foo happened"];
個(gè)人感想:會(huì)是很有用的新成員,尤其是在追求性能優(yōu)化下以及 React 項(xiàng)目中,gkdgkd。
.at() Relative Indexing Method (stage 3)
proposal-relative-indexing-method[11]提案引入了at()方法,用于獲取可索引類(Array, String, TypedArray)上指定位置的成員。
在過去 JavaScript 中一直缺乏負(fù)索引相關(guān)的支持,比如獲取數(shù)組的最后一個(gè)成員需要使用arr[arr.length-1],而無法使用arr[-1]。這主要是因?yàn)?JavaScript 中[]可以對(duì)所有對(duì)象使用,所以arr[-1]返回的是 key 為-1的屬性值,而非索引為-1(從后往前排序)的數(shù)組成員。
而要獲取數(shù)組的倒數(shù)第 N 個(gè)成員,通常使用的方法是arr[arr.length - N],或者arr.slice(-N)[0],兩種方法都有各自的缺陷,因此at()就來救場(chǎng)了。
另外,還存在獲取數(shù)組最后一個(gè)成員的提案,proposal-array-last[12] (stage1)與獲取數(shù)組最后一個(gè)符合條件的成員的提案 proposal-array-find-from-last[13]。
個(gè)人感想:來得有點(diǎn)晚,但也不算晚。
Temporal (stage 3)
proposal-temporal[14]主要是為了提供標(biāo)準(zhǔn)化的日期與時(shí)間 API,這一提案引入了一個(gè)全局的命名空間 Temporal(類似于 Math、Promise)來引入一系列現(xiàn)代化的日期 API(JavaScript 的 Date API 誰用誰知道嗷,也難怪社區(qū)那么多日期處理庫了),如:
Temporal.Instant獲取一個(gè)固定的時(shí)間對(duì)象:
const instant = Temporal.Instant.from("1969-07-20T20:17Z");
instant.toString(); // => '1969-07-20T20:17:00Z'
instant.epochMilliseconds; // => -14182980000
Temporal.PlainDate獲取 calendar date:
const date = Temporal.PlainDate.from({ year: 2006, month: 8, day: 24 }); // => 2006-08-24
date.year; // => 2006
date.inLeapYear; // => false
date.toString(); // => '2006-08-24'
Temporal.PlainTime獲取 wall-clock time(和上面一樣,不知道咋翻譯):
const time = Temporal.PlainTime.from({
hour: 19,
minute: 39,
second: 9,
millisecond: 68,
microsecond: 346,
nanosecond: 205,
}); // => 19:39:09.068346205
time.second; // => 9
time.toString(); // => '19:39:09.068346205'
Temporal.Duration獲取一段時(shí)間長度,用于比較時(shí)間有奇效
const duration = Temporal.Duration.from({
hours: 130,
minutes: 20,
});
duration.total({ unit: "second" }); // => 469200
更多細(xì)節(jié)參考ts39-proposal-temporal docs[15]。
個(gè)人感想:同樣,來得有點(diǎn)晚,但也不算晚。
Private Methods (stage 3)
private-methods[16] 提案為 JavaScript Class 引入了私有屬性、方法以及 getter/setter,不同于 TypeScript 中使用private語法,這一提案使用#語法來標(biāo)識(shí)私有成員,在阮老師的ES6 標(biāo)準(zhǔn)入門[17]中也提到了這一提案。
所以這個(gè)提案已經(jīng)過了多少年了...
參考阮老師給的例子:
class IncreasingCounter {
#count = 0;
get value() {
console.log("Getting the current value!");
return this.#count;
}
increment() {
this.#count++;
}
}
類似的,還有一個(gè)同樣處于 stage3 的提案proposal-class-fields[18]引入了static關(guān)鍵字。
個(gè)人感想:對(duì)我來說用處比較小,因?yàn)楫吘苟际菍?TS,幾乎沒有什么機(jī)會(huì)在 JavaScript 中寫 Class 了。但是這一提案成功被引入后,可能會(huì)使得 TS 到 JS 的編譯產(chǎn)物變化,即直接使用 JS 自身的static、#語法。比如現(xiàn)在這么一段 TS 代碼:
class A {
static x1 = 8;
}
編譯結(jié)果是:
"use strict";
class A {}
A.x1 = 8;
而在static被引入后,則會(huì)直接使用static語法。
Top-level await (stage4)
我記得這篇文章開始寫的時(shí)候,這個(gè)提案還在 stage3 的,我到底鴿了多久...
proposal-top-level-await[19]這個(gè)提案感覺就沒有啥展開描述的必要了,很多人應(yīng)該已經(jīng)用上了。簡(jiǎn)單地說,就是你的await語法不再和async強(qiáng)綁定了,你可以直接在應(yīng)用的最頂層使用await語法,Node 也從 14.8 開始支持了這一提案。
個(gè)人感想:可以少寫一個(gè) async 函數(shù)了,奈斯奧。
Import Assertions (stage 3)
proposal-import-assertions[20] 這一提案為導(dǎo)入語句新增了用于標(biāo)識(shí)模塊類型的斷言語句,語法如下:
import json from "./foo.json" assert { type: "json" };
import("foo.json", { assert: { type: "json" } });
注意,對(duì) JSON 模塊的導(dǎo)入最開始屬于這一提案的一部分,后續(xù)被獨(dú)立出來作為一個(gè)單獨(dú)的提案:proposal-json-modules[21]。
這一提案最初起源于為了在 JavaScript 中更便捷的導(dǎo)入 JSON 模塊,后續(xù)出于安全性考慮加上了import assertions來作為導(dǎo)入不可執(zhí)行模塊的必須條件。
這一提案同樣解決了模塊類型與其 MIME 類型不符的情況。
個(gè)人感想:和現(xiàn)在如火如荼的 ESM、Bundleless 工具應(yīng)該會(huì)有奇妙的化學(xué)反應(yīng)。
Decorators (stage 2)
proposal-decorators[24]這一提案...,或許是我們最熟悉的老朋友了。但是此裝飾器非彼裝飾器,歷時(shí)五年來裝飾器提案已經(jīng)走到了第三版,仍然卡在 stage 2。
這里引用我早前的一篇文章來簡(jiǎn)單講述下裝飾器的歷史:
首先我們需要知道,JS 與 TS 中的裝飾器不是一回事,JS 中的裝飾器目前依然停留在 stage 2[25] 階段,并且目前版本的草案與 TS 中的實(shí)現(xiàn)差異相當(dāng)之大(TS 是基于第一版,JS 目前已經(jīng)第三版了),所以二者最終的裝飾器實(shí)現(xiàn)必然有非常大的差異。
其次,裝飾器不是 TS 所提供的特性(如類型、接口),而是 TS 實(shí)現(xiàn)的 ECMAScript 提案(就像類的私有成員一樣)。TS 實(shí)際上只會(huì)對(duì)stage-3以上的語言提供支持,比如 TS3.7.5 引入了可選鏈(Optional chaining[26])與空值合并(Nullish-Coalescing[27])。而當(dāng) TS 引入裝飾器時(shí)(大約在 15 年左右),JS 中的裝飾器依然處于stage-1 階段。其原因是 TS 與 Angular 團(tuán)隊(duì) PY 成功了,Ng 團(tuán)隊(duì)不再維護(hù) [AtScript](<https://linbudu.top/posts/2020/08/10/atscript-playground[28]>),而 TS 引入了注解語法(Annotation)及相關(guān)特性。
但是并不需要擔(dān)心,即使裝飾器永遠(yuǎn)到達(dá)不了 stage-3/4 階段,它也不會(huì)消失的。有相當(dāng)多的框架都是裝飾器的重度用戶,如
Angular、Nest、Midway等。對(duì)于裝飾器的實(shí)現(xiàn)與編譯結(jié)果會(huì)始終保留,就像JSX一樣。如果你對(duì)它的歷史與發(fā)展方向有興趣,可以讀一讀 是否應(yīng)該在 production 里使用 typescript 的 decorator?[29](賀師俊賀老的回答)
個(gè)人感想:和類的私有成員、靜態(tài)成員提案一樣,目前使用最廣泛的還是 TS 中的裝飾器,但是二者的思路完全不同,因此我猜想原生裝飾器的提案不會(huì)影響 TypeScript 的編譯結(jié)果。
Iterator Helpers (stage 2)
proposal-iterator-helpers[30]提案為 ES 中的 Iterator 使用與消費(fèi)引入了一批新的接口,雖然實(shí)際上,如 Lodash 與Itertools[31](思路來自于 Python3 中的itertools[32])這樣的工具庫已經(jīng)提供了絕大部分能力,如 filter、filterMap 等。其他語言如 Rust、C#中也內(nèi)置了非常強(qiáng)大的 Iterator Helpers,見Prior Art[33]。
示例:
function* naturals() {
let i = 0;
while (true) {
yield i;
i += 1;
}
}
const evens = naturals().filter((n) => n % 2 === 0);
for (const even of evens) {
console.log(even, "is an even number");
}
個(gè)人感想:雖然目前很少會(huì)直接操作 Generator 和 Iterator 了,但這些畢竟是語言底部的東西,了解使用與機(jī)制還是有好處的。我上一次接觸 Iterator,還是為 Nx 編寫插件時(shí)為其提供 Async Iterator 接口,但也是直接囫圇吞棗的使用rxjs-for-await[34]這個(gè)庫。對(duì)這個(gè)提案的關(guān)注可能會(huì)相對(duì)少一些。
throw Expressions (stage 2)
proposal-throw-expressions[35]這一提案主要提供了const x = throw new Error()的能力,這并不是throw語法的替代品,更像是面向表達(dá)式(Expression-Oriented)的補(bǔ)齊。
function getEncoder(encoding) {
const encoder =
encoding === "utf8"
? new UTF8Encoder()
: encoding === "utf16le"
? new UTF16Encoder(false)
: encoding === "utf16be"
? new UTF16Encoder(true)
: throw new Error("Unsupported encoding");
}
個(gè)人感想:錯(cuò)誤處理又可以更自然美觀一些了,奈斯!
Set Methods (stage 2)
proposal-set-methods[36]這一提案為 Set 新增了一批新的方法,如:
intersection/union/difference:基于交集/并集/差集創(chuàng)建新的 SetisSubsetOf/isSupersetOf:判斷是否是子集/超集
個(gè)人感想:Set 的話用的比較少,但很明顯這些方法會(huì)是一個(gè)不錯(cuò)的能力增強(qiáng)。
Upsert(Map.prototype.emplace) (stage 2)
proposal-upsert[37]這一提案為 Map 引入了 emplace 方法,在當(dāng)前 Map 上的 key 已存在時(shí),執(zhí)行更新操作,否則執(zhí)行創(chuàng)建操作。
個(gè)人感想:確實(shí)是很甜的語法糖,感覺底層框架、工具庫用 Map 多一些。
Observable (stage 1)
proposal-observable[38]這一提案,其實(shí)懂的同學(xué)看到 Observable 已經(jīng)懂這個(gè)提案是干啥的了,它引入了 RxJS 中的 Observable、Observer(同樣是 next/error/complete/start)、Subscriber(next/error/complete)以及部分 Operators(RxJS:我直接好家伙),同樣支持高階 Observable,在被訂閱時(shí)才會(huì)開始推送數(shù)據(jù)(Lazy-Data-Emitting)。
function listen(element, eventName) {
return new Observable((observer) => {
// Create an event handler which sends data to the sink
let handler = (event) => observer.next(event);
// Attach the event handler
element.addEventListener(eventName, handler, true);
// Return a cleanup function which will cancel the event stream
return () => {
// Detach the event handler from the element
element.removeEventListener(eventName, handler, true);
};
});
}
估計(jì)是因?yàn)檫€在 stage1 的關(guān)系,目前支持的操作符只有 of、from,但按照這個(gè)趨勢(shì)下去 RxJS 中的大部分操作符都會(huì)被吸收過來。
個(gè)人感想:感覺需要非常久的時(shí)間才能看到未來結(jié)果,因?yàn)?RxJS 自身強(qiáng)大的多,海量操作符如果要吸收過來可能會(huì)是吃力不討好的。同時(shí),RxJS 的學(xué)習(xí)成本還是有的,我不認(rèn)為大家會(huì)因?yàn)樗晃盏?JS 語言原生就會(huì)紛紛開始學(xué)習(xí)相關(guān)概念。
Promise.try (stage 1)
proposal-promise-try[39]提案引入了Promise.try方法,這一方法其實(shí)很早就在bluebird[40]中提供了,其使用方式如下:
function getUserNameById(id) {
return Promise.try(function () {
if (typeof id !== "number") {
throw new Error("id must be a number");
}
return db.getUserById(id);
}).then((user) => {
return user.name;
});
}
Promise.try方法返回一個(gè) promise 實(shí)例,如果方法內(nèi)部拋出了錯(cuò)誤,則會(huì)走到.catch方法。上面的例子如果正常來寫,通常會(huì)這么寫:
function getUserNameById(id) {
return db.getUserById(id).then(function (user) {
return user.name;
});
}
看起來好像沒什么區(qū)別,但仔細(xì)想想,假設(shè)下面一個(gè)例子中,id 是錯(cuò)誤的,
db.getUserById(id)返回了空值,那么這樣 user.name 無法獲取,將會(huì)走.catch,但如果不返回空值而是拋出一個(gè)同步錯(cuò)誤?Promises 的錯(cuò)誤捕獲功能的工作原理是所有同步代碼都位于.then 中,這樣它就可以將其包裝在一個(gè)巨大的try/catch塊中(所以同步錯(cuò)誤都能走到.catch中)。但是在這個(gè)例子中,db.getUserById(id)并非位于.then語句中,這就導(dǎo)致了這里的同步錯(cuò)誤無法被捕獲。簡(jiǎn)單的說,如果僅使用.then,只有第一次異步操作后的同步錯(cuò)誤會(huì)被捕獲。
而是用Promise.try,它將捕獲db.getUserById(id)中的同步錯(cuò)誤(就像.then一樣,區(qū)別主要在 try 不需要前面跟著一個(gè) promise 實(shí)例),這樣子所有同步錯(cuò)誤就都能被捕獲了。
Do Expression (stage 1)
proposal-do-expressions[41]這個(gè)提案和throw Expressions 一樣,都是面向表達(dá)式(Expression-Oriented)的語法,函數(shù)式編程的重要優(yōu)勢(shì)之一。
看看示例代碼:
let x = do {
let tmp = f();
tmp * tmp + 1;
};
let y = do {
if (foo()) {
f();
} else if (bar()) {
g();
} else {
h();
}
};
對(duì)于像我一樣沒接觸過函數(shù)式編程的同學(xué),這種語法可能確實(shí)很新奇有趣,而且對(duì)能幫助更好的組織代碼。這一提案還存在著一些注意點(diǎn):
在 do {}中不能僅有聲明語句,或者是缺少 else 的 if,以及循環(huán)。空白的 do {}語句效果等同于void 0。await/yield標(biāo)識(shí)繼承自上下文
對(duì)于異步版本的do expression,存在一個(gè)尚未進(jìn)入的提案proposal-async-do-expressions[42],旨在使用async do {}的語法,如:
// at the top level of a script
(async do {
await readFile("in.txt");
let query = await ask("???");
// etc
});
Pipeline Operator (stage 1)
目前 star 最多的提案,似乎沒有之一?
proposal-pipeline-operator[43]提案引入了新的操作符|>,目前對(duì)于具體實(shí)現(xiàn)細(xì)節(jié)存在兩個(gè)不同的競(jìng)爭(zhēng)提案[44]。這一語法糖的主要目的是大大提升函數(shù)調(diào)用的可讀性,如doubleNumber(number)會(huì)變?yōu)?code style="font-size: 14.4px;font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;padding: 3px 5px;border-radius: 2px;margin-right: 2px;margin-left: 2px;color: rgb(255, 53, 2);background: rgb(248, 245, 236);word-break: break-all;line-height: 1.5;">number |> doubleNumber的形式,對(duì)于鏈?zhǔn)降倪B續(xù)函數(shù)調(diào)用更是有奇效,如:
function doubleSay(str) {
return str + ", " + str;
}
function capitalize(str) {
return str[0].toUpperCase() + str.substring(1);
}
function exclaim(str) {
return str + "!";
}
在管道操作符下,變?yōu)槿缦滦问剑?/p>
let result = exclaim(capitalize(doubleSay("hello")));
result; //=> "Hello, hello!"
let result = "hello" |> doubleSay |> capitalize |> exclaim;
result; //=> "Hello, hello!"
確實(shí)大大提高了不少可讀性對(duì)吧?你可能會(huì)想,上面都是單個(gè)入?yún)ⅲ嵌鄠€(gè)呢,如下圖示例:
function double(x) {
return x + x;
}
function add(x, y) {
return x + y;
}
function boundScore(min, max, score) {
return Math.max(min, Math.min(max, score));
}
let person = { score: 25 };
let newScore =
person.score
|> double
|> ((_) => add(7, _))
|> ((_) => boundScore(0, 100, _));
newScore; //=> 57
等同于
let newScore = boundScore(0, 100, add(7, double(person.score)));
_ 只是形參名稱,你可以使用任意的形參名稱。
Partial Application Syntax(stage 1)
proposal-partial-application[45]這一提案引入了新的柯里化(也屬于柯里化吧,如果你看了下面的例子覺得不屬于,請(qǐng)不要揍我)方式,即原本我們使用 bind 方法來預(yù)先固定一個(gè)函數(shù)的部分參數(shù),得到一個(gè)高階函數(shù):
function add(x, y) {
return x + y;
}
const addOne = add.bind(null, 1);
addOne(2); // 3
const addTen = (x) => add(x, 10);
addTen(2); // 12
使用 Partial Application Syntax,寫法會(huì)是這樣的:
const addOne = add(1, ?);
addOne(2); // 3
const addTen = add(?, 10);
addTen(2); // 12
我們上一個(gè)列舉的提案proposal-pipeline-operator[46],其實(shí)可以在 Partial Application Syntax 的幫助下變得更加便捷,尤其是在多參數(shù)情況下:
let person = { score: 25 };
let newScore = person.score |> double |> add(7, ?) |> boundScore(0, 100, ?);
目前的實(shí)現(xiàn)暫時(shí)不支持 await 關(guān)于更多細(xì)節(jié),參考 Pipeline operator: Seeking champions[47] 以及 Pipeline operator draft[48]。
await.opts (stage 1)
proposal-await.ops[49]這一提案為 await 引入了await.all/race/allSettled/any四個(gè)方法,來簡(jiǎn)化 Promise 的使用。實(shí)際上它們也正是Promise.all/race/allSettled/any的替代者,如:
// before
await Promise.all(users.map(async x => fetchProfile(x.id)))
// after
await.all users.map(async x => fetchProfile(x.id))
Array Unique (stage 1)
proposal-array-unique[50]主要是為了解決數(shù)組去重的問題,我們以往使用的[...new Set(array)] 無法很好的處理非原始類型的值,這一提案引入了Array.prototype.uniqueBy()方法來進(jìn)行數(shù)組的去重,類似于Lodash.uniqBy[51]。
個(gè)人感想:新的面試題出現(xiàn)了,請(qǐng)實(shí)現(xiàn)Array.prototype.uniqueBy()。2333,但是這個(gè)方法能原生支持還是很棒的。
JavaScript20 年-創(chuàng)立標(biāo)準(zhǔn): https://cn.history.js.org/part-2.html
[2]ECMA(European Computer Manufacturers Association,歐洲計(jì)算機(jī)制造商協(xié)會(huì)): https://www.ecma-international.org/
[3]2020 年 6 月發(fā)布的第 11 版: https://www.ecma-international.org/publications-and-standards/standards/ecma-262/
[4]2015 年發(fā)布的 ES6: https://262.ecma-international.org/6.0/index.html
[5]TC39 的 ECMA262 官網(wǎng): https://tc39.es/ecma262/
[6]ECMA-414: https://www.ecma-international.org/publications-and-standards/standards/ecma-414/
[7]ECMA-404: https://www.ecma-international.org/publications-and-standards/standards/ecma-404/
[8]ECMA267: https://www.ecma-international.org/publications-and-standards/standards/ecma-267/
[9]The TC39 process for ECMAScript features: https://2ality.com/2015/11/tc39-process.html
[10]proposal-record-tuple: https://github.com/tc39/proposal-record-tuple
[11]proposal-relative-indexing-method: https://github.com/tc39/proposal-relative-indexing-method
[12]proposal-array-last: https://github.com/tc39/proposal-array-last
[13]proposal-array-find-from-last: https://github.com/tc39/proposal-array-find-from-last
[14]proposal-temporal: https://github.com/tc39/proposal-temporal
[15]ts39-proposal-temporal docs: https://tc39.es/proposal-temporal/docs/index.html
[16]private-methods: https://github.com/tc39/proposal-private-methods
[17]ES6 標(biāo)準(zhǔn)入門: https://es6.ruanyifeng.com/#docs/class#%E7%A7%81%E6%9C%89%E5%B1%9E%E6%80%A7%E7%9A%84%E6%8F%90%E6%A1%88
[18]proposal-class-fields: https://github.com/tc39/proposal-class-fields
[19]proposal-top-level-await: https://github.com/tc39/proposal-top-level-await
[20]proposal-import-assertions: https://github.com/tc39/proposal-import-assertions
[21]proposal-json-modules: https://github.com/tc39/proposal-json-modules
[22]proposal-error-cause: https://github.com/tc39/proposal-error-cause
[23]吞吞老師: https://github.com/legendecas
[24]proposal-decorators: https://github.com/tc39/proposal-decorators
[25]stage 2: https://github.com/tc39/proposal-decorators
[26]Optional chaining: https://github.com/tc39/proposal-optional-chaining
[27]Nullish-Coalescing: https://github.com/tc39/proposal-nullish-coalescing
[28]AtScript: https://github.com/angular/atscript-playground
[29]是否應(yīng)該在 production 里使用 typescript 的 decorator?: https://www.zhihu.com/question/404724504
[30]proposal-iterator-helpers: https://github.com/tc39/proposal-iterator-helpers
[31]Itertools: https://www.npmjs.com/package/itertools
[32]itertools: https://docs.python.org/library/itertools.html
[33]Prior Art: https://github.com/tc39/proposal-iterator-helpers#prior-art
[34]rxjs-for-await: https://github.com/benlesh/rxjs-for-await
[35]proposal-throw-expressions: https://github.com/tc39/proposal-throw-expressions
[36]proposal-set-methods: https://github.com/tc39/proposal-set-methods
[37]proposal-upsert: https://github.com/tc39/proposal-upsert
[38]proposal-observable: https://github.com/tc39/proposal-observable
[39]proposal-promise-try: https://github.com/tc39/proposal-promise-try
[40]bluebird: http://bluebirdjs.com/docs/api/promise.try.html
[41]proposal-do-expressions: https://github.com/tc39/proposal-do-expressions
[42]proposal-async-do-expressions: https://github.com/tc39/proposal-async-do-expressions
[43]proposal-pipeline-operator: https://github.com/tc39/proposal-pipeline-operator
[44]兩個(gè)不同的競(jìng)爭(zhēng)提案: https://github.com/tc39/proposal-pipeline-operator/wiki
[45]proposal-partial-application: https://github.com/tc39/proposal-partial-application
[46]proposal-pipeline-operator: https://github.com/tc39/proposal-pipeline-operator
[47]Pipeline operator: Seeking champions: https://docs.google.com/presentation/d/1for4EIeuVpYUxnmwIwUuAmHhZAYOOVwlcKXAnZxhh4Q/edit#slide=id.g79e9b7164e_0_531
[48]Pipeline operator draft: https://tc39.es/proposal-pipeline-operator/
[49]proposal-await.ops: https://github.com/tc39/proposal-await.ops
[50]proposal-array-unique: https://github.com/tc39/proposal-array-unique
[51]...new Set(array)]無法很好的處理非原始類型的值,這一提案引入了Array.prototype.uniqueBy()`方法來進(jìn)行數(shù)組的去重,類似于[Lodash.uniqBy: https://www.lodashjs.com/docs/lodash.uniqBy
最后
如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:
點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)
歡迎加我微信「 sherlocked_93 」拉你進(jìn)技術(shù)群,長期交流學(xué)習(xí)...
關(guān)注公眾號(hào)「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。

