摘要:由很多令人興奮的功能,如對象的解析與剩余,異步迭代器,方法和更好的正則表達(dá)式支持。迭代可以是任何遵循迭代器協(xié)議的對象。迭代器方法應(yīng)該返回一個具有方法的對象。
原文:TC39, ECMAScript, and the Future of JavaScript
作者:Nicolás Bevacqua
很榮幸能夠和 Nicolás Bevacqua 同臺分享。Nicolás Bevacqua 分享了《the Future of Writing JavaScript 》,我在其后分享了《面向前端開發(fā)者的V8性能優(yōu)化》。如果想了解更多 V8 知識可以關(guān)注我的專欄:V8 引擎。
由于 Nicolás Bevacqua 是英文分享,現(xiàn)場由很多聽眾都沒有太明白,會后我聯(lián)系了 Nicolás Bevacqua 爭得大神同意后將其文章翻譯為中文。
大神微信玩的很溜,很快就學(xué)會了搶紅包。
再次感謝 Nicolás Bevacqua 的精彩分享。
譯文:
TC39 是什么?上周,我在中國深圳的騰訊前端大會上發(fā)表了與本文同名的演講。在這篇文章中,我根據(jù) PonyFoo 網(wǎng)站的格式重新編輯了一遍。我希望你喜歡它!
TC39 指的是技術(shù)委員會(Technical Committee)第 39 號。它是 ECMA 的一部分,ECMA 是 “ECMAScript” 規(guī)范下的 JavaScript 語言標(biāo)準(zhǔn)化的機構(gòu)。
ECMAScript 規(guī)范定義了 JavaScript 如何一步一步的進(jìn)化、發(fā)展。其中規(guī)定了:
字符串 "A" 為什么是 NaN
字符串 "A" 為什么不等于 NaN
NaN 為什么是 NaN,但卻不等于 NaN
并介紹了為什么 Number.isNaN 是一個很好的 idea ...
isNaN(NaN) // true isNaN("A") // true "A" == NaN // false "A" === NaN // false NaN === NaN // false // … 解決方案! Number.isNaN("A") // false Number.isNaN(NaN) // true
它還解釋了正零與負(fù)零什么情況下相等,什么情況下不相等。。。
+0 == -0 // true +0 === -0 // true 1/+0 === 1 / -0 // false
而且 js 中還有很多奇技淫巧,例如只使用感嘆號、小括號、方括號和加號來編碼任何有效的 JavaScript 表達(dá)式??梢栽?JSFuck 網(wǎng)站了解更多關(guān)于如何只使用 +!()[] 編寫 JavaScript 代碼的技巧。
不論如何,TC39 所做的不懈努力是難能可貴的。
TC39 遵循的原則是:分階段加入不同的語言特性。一旦提案成熟,TC39 會根據(jù)提案中的變動來更新規(guī)范。直到最近,TC39 依然依賴基于 Microsoft Word 的比較傳統(tǒng)的工作流程。但 ES3 出來之后,他們花了十年時間,幾乎沒有任何改變,使其達(dá)到規(guī)范。之后,ES6 又花了四年才能實現(xiàn)。
顯然,他們的流程必須改善。
自 ES6 出來之后,他們精簡了提案的修訂過程,以滿足現(xiàn)代化開發(fā)的需求。新流程使用 HTML 的超集來格式化提案。他們使用 GitHub pull requests,這有助于增加社區(qū)的參與,并且提出的提案數(shù)量也增加了。這個規(guī)范現(xiàn)在是一個 living standard,這意味著提案會更快,而且我們也不用等待新版本的規(guī)范出來。
新流程涉及四個不同的 Stage。一個提案越成熟,越有可能最終將其納入規(guī)范。
Stage 0任何尚未提交作為正式提案的討論、想法變更或者補充都被認(rèn)為是第 0 階段的“稻草人”提案。只有 TC39 的成員可以創(chuàng)建這些提案,而且今天就有若干活躍的“稻草人”提案。
目前在 Stage 0 的提案包括異步操作的 cancellation tokens , Zones 作為 Angular 團隊的一員,提供了很多建議。Stage 0 包括了很多一直沒有進(jìn)入 Stage 1 的提案。
在這篇文章的后面,我們將仔細(xì)分析一部分提案。
Stage 1在 Stage 1,提案已經(jīng)被正式化,并期望解決此問題,還需要觀察與其他提案的相互影響。在這個階段的提案確定了一個分散的問題,并為這個問題提供了具體的解決方案。
Stage 1 提議通常包括高階 API 描述(high level AP),使用示例以及內(nèi)部語義和算法的討論。這些建議在通過這一過程時可能會發(fā)生重大變化。
Stage 1 目前提案的例子包括:Observable、do 表達(dá)式、生成器箭頭函數(shù)、Promise.try。
Stage 2Stage 2 的提案應(yīng)提供規(guī)范初稿。
此時,語言的實現(xiàn)者開始觀察 runtime 的具體實現(xiàn)是否合理。該實現(xiàn)可以使用 polyfill 的方式,以便使代碼可在 runtime 中的行為負(fù)責(zé)規(guī)范的定義; javascript 引擎的實現(xiàn)為提案提供了原生支持; 或者可以 Babel 這樣的編譯時編譯器來支持。
目前 Stage 2 階段的提案有 public class fields、private class fields、decorators、Promise#finally、等等。
Stage 3Stage 3 提案是建議的候選提案。在這個高級階段,規(guī)范的編輯人員和評審人員必須在最終規(guī)范上簽字。Stage 3 的提案不會有太大的改變,在對外發(fā)布之前只是修正一些問題。
語言的實現(xiàn)者也應(yīng)該對此提案感興趣 - 如果只是提案卻沒有具體實現(xiàn)去支持這個提案,那么這個提案早就胎死腹中了。事實上,提案至少具有一個瀏覽器實現(xiàn)、友好的 polyfill或者由像 Babel 這樣的構(gòu)建時編譯器支持。
Stage 3 由很多令人興奮的功能,如對象的解析與剩余,異步迭代器,import() 方法和更好的 Unicode 正則表達(dá)式支持。
Stage 4最后,當(dāng)規(guī)范的實現(xiàn)至少通過兩個驗收測試時,提案進(jìn)入 Stage 4。
進(jìn)入 Stage 4 的提案將包含在 ECMAScript 的下一個修訂版中。
異步函數(shù),Array#includes 和 冪運算符 是 Stage 4 的一些特性。
保持最新 Staying Up To Date我(原文作者)創(chuàng)建了一個網(wǎng)站,用來展示當(dāng)前提案的列表。它描述了他們在什么階段,并鏈接到每個提案,以便您可以更多地了解它們。
網(wǎng)址為 proptt39.now.sh。
目前,每年都有新的正式規(guī)范版本,但精簡的流程也意味著正式版本變得越來越不相關(guān)。現(xiàn)在重點放在提案階段,我們可以預(yù)測,在 ES6 之后,對該標(biāo)準(zhǔn)的具體修訂的引用將變得不常見。
提案 Proposals我們來看一些目前正在開發(fā)的最有趣的提案。
Array#includes (Stage 4)在介紹 Array#includes 之前,我們不得不依賴 Array#indexOf 函數(shù),并檢查索引是否超出范圍,以確定元素是否屬于數(shù)組。
隨著 Array#includes 進(jìn)入 Stage 4,我們可以使用 Array#includes 來代替。它補充了 ES6 的 Array#find 和 Array#findIndex。
[1, 2].indexOf(2) !== -1 // true [1, 2].indexOf(3) !== -1 // false [1, 2].includes(2) // true [1, 2].includes(3) // false異步函數(shù)(Stage 4)
當(dāng)我們使用 Promise 時,我們經(jīng)??紤]執(zhí)行線程。我們有一個異步任務(wù) fetch,其他任務(wù)依賴于 fetch 的響應(yīng),但在收到該數(shù)據(jù)之前程序時阻塞的。
在下面的例子中,我們從 API 中獲取產(chǎn)品列表,該列表返回一個 Promise。當(dāng) fetch 相應(yīng)之后,Promise 被 resolve。然后,我們將響應(yīng)流作為 JSON 讀取,并使用響應(yīng)中的數(shù)據(jù)更新視圖。如果在此過程中發(fā)生任何錯誤,我們可以將其記錄到控制臺,以了解發(fā)生了什么。
fetch("/api/products") .then(response => response.json()) .then(data => { updateView(data) }) .catch(err => { console.log("Update failed", err) })
異步函數(shù)提供了語法糖,可以用來改進(jìn)我們基于 Promise 的代碼。我們開始逐行改變以上基于 Promise 的代碼。我們可以使用 await 關(guān)鍵字。當(dāng)我們 await 一個 Promise 時,我們得到 Promise 的 fulled 狀態(tài)的值。
Promise 代碼的意思是:“我想執(zhí)行這個操作,然后(then)在其他操作中使用它的結(jié)果”。
同時,await 有效地反轉(zhuǎn)了這個意思,使得它更像:“我想要取得這個操作的結(jié)果”。我喜歡,因為它聽起來更簡單。
在我們的示例中,響應(yīng)對象是我們之后獲取的,所以我們將等待(await)獲取(fetch)操作的結(jié)果,并賦值給 response 變量,而不是使用 promise 的 then。
原文:we’ll flip things over and assigned the result of await fetch to the response variable
+ const response = await fetch("/api/products") - fetch("/api/products") .then(response => response.json()) .then(data => { updateView(data) }) .catch(err => { console.log("Update failed", err) })
我們給 response.json() 同樣的待遇。我們 await 上一次的操作并將其賦值給 data 變量。
const response = await fetch("/api/products") + const data = await response.json() - .then(response => response.json()) .then(data => { updateView(data) }) .catch(err => { console.log("Update failed", err) })
既然 then 鏈已經(jīng)消失了,我們就可以直接調(diào)用 updateView 語句了,因為我們已經(jīng)到了之前代碼中的 Promise then 鏈的盡頭,我們不需要等待任何其他的 Promise。
const response = await fetch("/api/products") const data = await response.json() + updateView(data) - .then(data => { - updateView(data) - }) .catch(err => { console.log("Update failed", err) })
現(xiàn)在我們可以使用 try/catch 塊,而不是 .catch,這使得我們的代碼更加語義化。
+ try { const response = await fetch("/api/products") const data = await response.json() updateView(data) + } catch(err) { - .catch(err => { console.log("Update failed", err) + } - )}
一個限制是 await 只能在異步函數(shù)內(nèi)使用。
+ async function run() { try { const response = await fetch("/api/products") const data = await response.json() updateView(data) } catch(err) { console.log("Update failed", err) } + }
但是,我們可以將異步函數(shù)轉(zhuǎn)換為自調(diào)用函數(shù)表達(dá)式。如果我們將頂級代碼包在這樣的表達(dá)式中,我們可以在代碼中的任何地方使用 await 表達(dá)式。
一些社區(qū)希望原生支持頂級塊作用于的 await,而另外一些人則認(rèn)為這會對用戶造成負(fù)面影響,因為一些庫可能會阻塞異步加載,從而大大減緩了我們應(yīng)用程序的加載時間。
+ (async () => { - async function run() { try { const response = await fetch("/api/products") const data = await response.json() updateView(data) } catch(err) { console.log("Update failed", err) } + })() - }
就個人而言,我認(rèn)為在 JavaScript 性能中已經(jīng)有足夠的空間來應(yīng)對這種愚蠢的事情,來優(yōu)化初始化的庫使用 await 的行為。
請注意,您也可以在 non-promise 的值前面使用 await,甚至編寫代碼 await (2 + 3)。在這種情況下,(2 + 3) 表達(dá)的結(jié)果會被包在 Promise 中,作為 Promise 的最終值。5 成為這個 await 表達(dá)式的結(jié)果。
請注意,await 加上任何 JavaScript 表達(dá)式也是一個表達(dá)式。這意味著我們不限制 await 語句的賦值操作,而且我們也可以把 await 函數(shù)調(diào)用作為模板文字插值的一部分。
`Price: ${ await getPrice() }`
或作為另一個函數(shù)調(diào)用的一部分...
renderView(await getPrice())
甚至作為數(shù)學(xué)表達(dá)式的一部分。
2 * (await getPrice())
最后,不管它們的內(nèi)容如何,??異步函數(shù)總是返回一個 Promise。這意味著我們可以添加 .then 或 .catch 等異步功能,也可以使用 await 獲取最終的結(jié)果。
const sleep = delay => new Promise(resolve => setTimeout(resolve, delay) ) const slowLog = async (...terms) => { await sleep(2000) console.log(...terms) } slowLog("Well that was underwhelming") .then(() => console.log("Nailed it!")) .catch(reason => console.error("Failed", reason))
正如您所期望的那樣,返回的 Promise 與 async 函數(shù)返回的值進(jìn)行運算,或者被 catch 函數(shù)來處理任何未捕獲的異常。
異步迭代器(Stage 3)異步迭代器已經(jīng)進(jìn)入了 Stage 3。在了解異步迭代器之前,讓我們簡單介紹一下 ES6 中引入的迭代。迭代可以是任何遵循迭代器協(xié)議的對象。
為了使對象可以迭代,我們定義一個 Symbol.iterator 方法。迭代器方法應(yīng)該返回一個具有 next 方法的對象。這個對象描述了我們的 iterable 的順序。當(dāng)對象被迭代時,每當(dāng)我們需要讀取序列中的下一個元素時,將調(diào)用 next 方法。value 用來獲取序列中每一個對象的值。當(dāng)返回的對象被標(biāo)記為 done,序列結(jié)束。
const list = { [Symbol.iterator]() { let i = 0 return { next: () => ({ value: i++, done: i > 5 }) } } } [...list] // <- [0, 1, 2, 3, 4] Array.from(list) // <- [0, 1, 2, 3, 4] for (const i of list) { // <- 0, 1, 2, 3, 4 }
可以使用 Array.from 或使用擴展操作符使用 Iterables 。它們也可以通過使用 for..of 循環(huán)來遍歷元素序列。
異步迭代器只有一點點不同。在這個提議下,一個對象通過 Symbol.asyncIterator 來表示它們是異步迭代的。異步迭代器的方法簽名與常規(guī)迭代器的約定略有不同:該 next 方法需要返回 包裝了 { value, done } 的 Promise,而不是 { value, done } 直接返回。
const list = { [Symbol.asyncIterator]() { let i = 0 return { next: () => Promise.resolve({ value: i++, done: i > 5 }) } } }
這種簡單的變化非常優(yōu)雅,因為 Promise 可以很容易地代表序列的最終元素。
異步迭代不能與數(shù)組擴展運算符、Array.from、for..of 一起使用,因為這三個都專門用于同步迭代。
這個提案也引入了一個新的 for await..of 結(jié)構(gòu)。它可以用于在異步迭代序列上語義地迭代。
for await (const i of items) { // <- 0, 1, 2, 3, 4 }
請注意,該 for await..of 結(jié)構(gòu)只能在異步函數(shù)中使用。否則我們會得到語法錯誤。就像任何其他異步函數(shù)一樣,我們也可以在我們的循環(huán)周圍或內(nèi)部使用 try/catch 塊 for await..of。
async function readItems() { for await (const i of items) { // <- 0, 1, 2, 3, 4 } }
更進(jìn)一步。還有異步生成器函數(shù)。與普通生成器函數(shù)有些相似,異步生成器函數(shù)不僅支持 async await 語義,還允許 await 語句以及 for await..of。
(原文第一段:The rabbit hole goes deeper of course. 這是愛麗絲夢游仙境的梗嗎?)
async function* getProducts(categoryUrl) { const listReq = await fetch(categoryUrl) const list = await listReq.json() for (const product of list) { const productReq = await product.url const product = await productReq.json() yield product } }
在異步生成器函數(shù)中,我們可以使用 yield* 與其他異步發(fā)生器和普通的發(fā)生器一起使用。當(dāng)調(diào)用時,異步生成器函數(shù)返回異步生成器對象,其方法返回包裹了 { value, done } 的 Promise,而不是 { value, done }。
最后,異步生成器對象可以被使用在 for await..of,就像異步迭代一樣。這是因為異步生成器對象是異步迭代,就像普通生成器對象是普通的迭代。
async function readProducts() { const g = getProducts(category) for await (const product of g) { // use product details } }對象解構(gòu)與剩余(Stage 3)
從 ES6 開始,我們使用 Object.assign 將屬性從一個或多個源對象復(fù)制到一個目標(biāo)對象上。在下一個例子中,我們將一些屬性復(fù)制到一個空的對象上。
Object.assign( {}, { a: "a" }, { b: "b" }, { a: "c" } )
對象解構(gòu)(spread)提議允許我們使用純語法編寫等效的代碼。我們從一個空對象開始,Object.assign 隱含在語法中。
{ ...{ a: "a" }, ...{ b: "b" }, ...{ a: "c" } } // <- { a: "c", b: "b" }
和對象解構(gòu)相反的還有對象剩余,類似數(shù)組的剩余參數(shù)。當(dāng)對對象進(jìn)行解構(gòu)時,我們可以使用對象擴展運算符將模式中未明確命名的屬性重建為另一個對象。
在以下示例中,id 顯式命名,不會包含在剩余對象中。對象剩余(rest)可以從字面上讀取為“所有其他屬性都轉(zhuǎn)到一個名為 rest 的對象”,當(dāng)然,變量名稱供您選擇。
const item = { id: "4fe09c27", name: "Banana", amount: 3 } const { id, ...rest } = item // <- { name: "Banana", amount: 3 }
在函數(shù)參數(shù)列表中解析對象時,我們也可以使用對象剩余屬性。
function print({ id, ...rest }) { console.log(rest) } print({ id: "4fe09c27", name: "Banana" }) // <- { name: "Banana" }動態(tài) import()(Stage 3)
ES6 引入了原生 JavaScript 模塊。與 CommonJS 類似,JavaScript 模塊選擇了靜態(tài)語法。這樣開發(fā)工具有更簡單的方式從靜態(tài)源碼中分析和構(gòu)建依賴樹,這使它成為一個很好的默認(rèn)選項。
import markdown from "./markdown" // … export default compile
然而,作為開發(fā)人員,我們并不總是知道我們需要提前導(dǎo)入的模塊。對于這些情況,例如,當(dāng)我們依賴本地化來加載具有用戶語言的字符串的模塊時,Stage 3 的動態(tài) import() 提案就很有用了。
import() 運行時動態(tài)加載模塊。它為模塊的命名空間對象返回 Promise,當(dāng)獲取該對象時,系統(tǒng)將解析和執(zhí)行所請求的模塊及其所有依賴項。如果模塊加載失敗,Promise 將被拒絕。
import(`./i18n.${ navigator.language }.js`) .then(module => console.log(module.messages)) .catch(reason => console.error(reason))
未完。。。。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/83771.html
摘要:前端日報精選譯,和的未來學(xué)習(xí)筆記箭頭函數(shù)學(xué)習(xí)筆記教程柵格布局卷土重來,用還是為什么我會選擇而不是眾成翻譯原生開發(fā)入門完全教程從零到壹全棧部落中文一個端帶文件路徑和顏色的攻城方略譯使用提高應(yīng)用程序的種方式中自定義操作符修仙 2017-07-02 前端日報 精選 [譯] TC39,ECMAScript 和 JavaScript 的未來(Part 1)ES6學(xué)習(xí)筆記:箭頭函數(shù)_ES6, Ja...
摘要:隨后,它出現(xiàn)在公司之后的瀏覽器,以及從微軟從起發(fā)布的所有瀏覽器上。標(biāo)準(zhǔn)的第版在年月的大會上被表決接受。第版在年月底大會上被采納。 前言 ??本系列譯文的初衷旨在希望更多人能夠了解關(guān)于JS的一些基本概念,遇到原理性的問題時多去翻翻文檔,而不是在社區(qū)無休止的重復(fù)提出某些在文檔中能夠非常方便快捷就能找到的東西。 ??精力和水平有限,所以暫時只打算嘗試翻譯前面幾章概括性的介紹,同時后面的章節(jié)大...
摘要:他們的計劃是,使用微軟開發(fā)者們所習(xí)慣的其他語言的開發(fā)工具所支持的靜態(tài)類型,得到更好的代碼。在微軟內(nèi)部,被和以及團隊所使用,而且它被系的等公司使用。標(biāo)準(zhǔn)的編輯,同時也是微軟項目高級經(jīng)理的也同意。 本文轉(zhuǎn)載自:眾成翻譯譯者:文藺鏈接:http://www.zcfy.cc/article/895原文:http://thenewstack.io/javascript-transpilers-n...
摘要:前端日報精選精讀與提案知乎專欄第期認(rèn)識引擎記錄一次利用工具進(jìn)行性能優(yōu)化的真實案例簡書中的使用規(guī)則教程繼承的實現(xiàn)方法個人文章中文譯組件渲染性能探索個人文章周刊第期表單性能的改進(jìn)實踐知乎專欄簡單可重用的圖表庫知乎專欄 2017-07-08 前端日報 精選 精讀 TC39 與 ECMAScript 提案 - 知乎專欄【第989期】認(rèn)識 V8 引擎記錄一次利用 Timeline/Perform...
摘要:每個引擎開始實現(xiàn)每次發(fā)布后指定的更改。每個提案都是最初提出的或。此建議的目的只是為了避免在提案被放棄或徹底更改時出現(xiàn)問題。這將限制對這些檢查的需求,從而限制性能損失。這與這就是新提案無效合并的用武之地。這是因為價值已成為承諾。 讓我們來看看JavaScript中一些有用的即將推出的功能。您將看到他們的語法,鏈接以及時了解他們的進(jìn)度,我們將編寫一個小型測試套件,以展示如何立即開始使用這些...
閱讀 865·2021-10-09 09:44
閱讀 757·2019-08-30 13:55
閱讀 3220·2019-08-29 15:07
閱讀 3278·2019-08-29 13:09
閱讀 2466·2019-08-29 11:10
閱讀 1358·2019-08-26 14:05
閱讀 3682·2019-08-26 13:57
閱讀 2259·2019-08-23 16:42