Javascript 的新功能-Part 1[每日前端夜話0xC6]
- 2019 年 10 月 7 日
- 筆記
作者:Deepak Gupta
翻譯:瘋狂的技術宅
來源:medium
JavaScript 的應用領域已經從 Web 瀏覽器擴展到所有需要編程的地方。
- Node.js — 用於CLI和伺服器。
- Electron — 用於跨平台的桌面應用程式。
- React native — 用於跨平台的移動應用。
- IoT — 低成本物聯網設備現在開始支援 javascript。
最近更新的 V8 引擎使性能提升了不少。JavaScript 解析速度提高了 2 倍甚至更快,從node v8.0開始,node v11以上版本的平均速度比 node v8.0 提高了 11 倍。記憶體消耗減少了 20%。在性能和可用性上有了全面改善。
在本文中,我們將看到一些可以在Chrome瀏覽器(版本 ≥ 76)或 Node.js(版本 ≥ 11)CLI 中測試的 ES10 強大功能。
私有類欄位
在ES6之前,我們無法直接申請 private
屬性。是的,有下劃線約定(_propertyName
)、閉包、 symbols 或 WeakMaps 等方法。
但現在私有類欄位可以使用哈希前綴 #
來定義。讓我們通過實例學習它
class Test { a = 1; // .a is public #b = 2; // .#b is private static #c = 3; // .#c is private and static incB() { this.#b++; } } const testInstance = new Test(); // runs OK testInstance.incB(); // error - private property cannot be modified outside class testInstance.#b = 0;
注意:截至目前,沒有辦法定義私有函數,儘管 TC39 第 3 階段:建議草案【https://github.com/tc39/proposal-private-methods】建議在名字上使用散列前綴
#
。?
String.matchAll()?
如果我有一個字元串,其中有多個全局正則表達式捕獲組,我經常想要遍歷所有匹配。目前,我的選擇有以下幾種:
- RegExp.prototype.exec() with /g — 我們可以稱之為
.exec()
多次獲得一個正則表達式的匹配。它為每個匹配返回一個匹配對象,最後返回 null。 - String.prototype.match() with /g — 如果我們通過
.match()
使用正則表達式,設置其標誌為/g
,你會得到一個完全匹配的數組。 - String.prototype.split() — 如果我們使用分割字元串和正則表達式來指定分隔符,並且它至少包含一個捕獲組,那麼
.split()
將返回一個子串交錯的數組。
上述方法的問題在於,只有在正則表達式上設置 /g
並且每次匹配時對正則表達式的屬性 .lastIndex
進行更改時,它們才起作用。這使得在多個位置使用相同的正則表達式存在風險。
matchAll() 能夠幫助解決以上所有問題。讓我們看看它的定義和使用:
給定字元串和正則表達式,.matchAll()
返回與正則表達式匹配的所有結果,包括捕獲組。
let regexp = /t(e)(st(d?))/g; let str = 'test1test2';let array = [...str.matchAll(regexp)];console.log(array[0]); // expected output: Array ["test1", "e", "st1", "1"]
注意:
.matchAll()
返回一個迭代器,但它不是真正的可重啟迭代器。也就是說一旦結果耗盡,則需要再次調用該方法並創建一個新的迭代器。
數字分隔?
如果你一直在努力去讀較長的數字序列,那麼這就是你要找的。
數字分隔符使人眼能夠快速解析,尤其是當有很多重複的數字時:
1000000000000 -> 1_000_000_000_000 1019436871.42 -> 1_019_436_871.42
現在,更容易說出第一個數字是1萬億,而第二個數字是10億。
這也適用於其他進位,例如:
const fileSystemPermission = 0b111_111_000; const bytes = 0b1111_10101011_11110000_00001101; const words = 0xFAB_F00D;
你還可以用在分數和指數中:
const massOfElectronInKg = 9.109_383_56e-31; const trillionInShortScale = 1e1_2;
注意:解析帶有
_
分隔的整數可能很棘手,因為Number('123_456')
會給出 NAN,而parseInt('123_456')
則給出123
。
BigInt?
BigInts
是 JavaScript 中的一種新的數字原語,可以表示精度比2⁵³-1更大的整數。使用 BigInts,你可以安全地存儲和操作大整數,甚至可以超出 Numbers 的安全整數限制。
BigInts
可以正確執行整數運算而不會溢出。讓我們通過一個例子來理解:
const max = Number.MAX_SAFE_INTEGER; // 9007199254740991 max+1; // 9007199254740992 max+2; // 9007199254740992
我們可以看到 max + 1
產生的結果與 max + 2
相同。
任何超出安全整數範圍(即從 Number.MIN_SAFE_INTEGER
到 Number.MAX_SAFE_INTEGER
)的整數的計算都可能會失去精度。所以我們只能依賴安全範圍內的數字整型的值。
BigInts
應運而生,可以通過將 n
後綴添加到整數文字中來創建 BigInts
。例如,123
變成 123n
,或者全局 BigInt(number)
函數可用於將 Number
轉換為 BigInts
。
讓我們重新看一下上面的 BigInt
例子
BigInt(Number.MAX_SAFE_INTEGER) + 2n; // 9007199254740993ntypeof 123n // "bigint2"
注意:數字分隔符對於BigInts尤其有用,例如:
const massOfEarthInKg = 6_000_000_000_000_000_000_000_000n;
BigInts
支援最常見的運算符。二進位 +
,-
,*
和 **
均按預期工作。/
和 %
工作時根據需要四捨五入。
(7 + 6 - 5) * 4 ** 3 / 2 % 3; // → 1 (7n + 6n - 5n) * 4n ** 3n / 2n % 3n; // → 1n
注意:它不允許在
BigInts
和Numbers
之間進行混合運算。
BigInt 的語言環境字元串?
toLocaleString()
方法返回一個字元串,該字元串具有 BigInt 的語言敏感表示形式。
let bigint = 123456789123456789n; // 德國使用 thousands console.log(bigint.toLocaleString('de-DE')); // → 123.456.789.123.456.789 //在大多數說阿拉伯語的國家中,阿拉伯語使用東部阿拉伯數字 console.log(bigint.toLocaleString('ar-EG')); // → ١٢٣٬٤٥٦٬٧٨٩٬١٢٣٬٤٥٦٬٧٨٩ // 印度使用 thousands/lakh/crore 分隔符 console.log(bigint.toLocaleString('en-IN')); // → 1,23,45,67,89,12,34,56,789 // nu 擴展用於請求編號系統,例如 中文數字 console.log(bigint.toLocaleString('zh-Hans-CN-u-nu-hanidec')); // → 一二三,四五六,七八九,一二三,四五六,七八九 // 請求不支援的語言(例如巴厘語)時,請使用後備語言(在這種情況下為印尼語) console.log(bigint.toLocaleString(['ban', 'id'])); // → 123.456.789.123.456.789
globalThis 關鍵字?
JavaScript 的變數作用域被嵌套並形成樹結構,其根是全局作用域,this
關鍵字的值是對 「擁有」 當前正在執行的程式碼或所查看函數的對象的引用。
要了解有關此關鍵字和全局作用一的更多資訊,請閱讀以下文章
Scopes in Javascript【https://medium.com/datadriveninvestor/still-confused-in-js-scopes-f7dae62c16ee】 Understanding Javascript 『this』 keyword (Context)【https://medium.com/datadriveninvestor/javascript-context-this-keyword-9a78a19d5786】
通常要弄清楚全局作用域,我們使用這樣的函數
const getGlobalThis = () => { // 在 webworker 或 service worker 中 if (typeof self !== 'undefined') return self; // 在瀏覽器中 if (typeof window !== 'undefined') return window; // 在 Node.js 中 if (typeof global !== 'undefined') return global; // 獨立的 JavaScript shell if (typeof this !== 'undefined') return this; throw new Error('Unable to locate global object'); };const theGlobalThis = getGlobalThis();
以上函數並不涵蓋全局變數的所有情況。
- 如果使用
strict
,則其值是undefined
- 當我們在 javascript 中形成捆綁包時,通常會在一些可能與此全局程式碼不同的程式碼下進行包裝。
- 在獨立的 JavaScript 引擎 shell 環境中,以上程式碼將不起作用
為了解決上述問題,引入了 globalThis
關鍵字,該關鍵字可以在任何環境下隨時返回全局對象。
注意:為了保持向後兼容,現在全局對象被認為是 JavaScript 無法消除的錯誤。它會對性能產生負面影響,並經常使人困惑。
Promise.allSettled()?
如果你想知道 JavaScript Promise 的用途,請查看此內容 —— JavaScript Promises:簡介【https://developers.google.com/web/fundamentals/primers/promises】。
Promise 是 JavaScript 向你承諾工作將要完成的方式(如果工作無法完成,則可能會失敗)。
新方法會返回一個 Promise ,它會在所有給定的 Promise 均已解決(即已解決或拒絕)之後解決,並帶有一系列對象,一個對象描述一個 Promise 的結果。
const promise1 = Promise.resolve(3); const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo')); const promises = [promise1, promise2];Promise.allSettled(promises). then((results) => results.forEach((result) => console.log(result.status))); // 預期輸出: // "fulfilled" // "rejected"
這與 Promise.all
不同,因為 Promise.all
在可迭代對象中的 Promise 被拒絕後就立即拒絕。
下面是當前支援的 promise 方法的比較
|
Short-circuit? |
Short-circuits on? |
Fulfilled on? |
Rejected on? |
---|---|---|---|---|
Promise.all |
✅ |
First rejected promise |
All promise fulfilled |
First rejected promise |
Promise.allSettled |
❌ |
N/A |
Always |
N/A |
Promise.race |
✅ |
First settled |
First promise fulfilled |
First rejected promise |
動態導入?

靜態與動態導入
這個很瘋狂,在我們深入研究它之前,先看看靜態導入是什麼。
靜態導入僅接受字元串文字作為模組說明符,並通過運行前的「鏈接」過程將綁定引入本地作用域。
靜態的 import
語法只能在文件的頂層使用。
import * as module from './utils.mjs';
靜態 import
可以啟用重要的用例,如靜態分析、捆綁工具、和tree-shaking。
但是以下這些:
- 按需(或有條件)導入模組
- 在運行時計算模組說明符
- 從常規腳本(而不是模組)中導入模組
在動態導入出現之前是不可能的 — import(moduleSpecifier)
返回所請求模組的模組命名空間對象的promise,它是在提取、實例化和評估模組的所有依賴關係以及模組本身之後才創建的。
<script type="module"> (async () => { const moduleSpecifier = './utils.mjs'; const module = await import(moduleSpecifier) module.default(); // → logs 'Hi from the default export!' module.doStuff(); // → logs 'Doing stuff…' })(); </script>
注意:對於初始化繪製依賴項,尤其是首屏內容時請使用靜態
import
。在其他情況下,考慮用動態import()
按需載入依賴項。
穩定排序(現在能夠得到一致和可靠的結果)?
穩定在演算法意義【https://en.wikipedia.org/wiki/Sorting_algorithm#Stability】上的意思是:它是保留順序,還是僅保證項目「相等」?
讓我們通過一個例子理解它:
const people = [ {name: 'Gary', age: 20}, {name: 'Ann', age: 20}, {name: 'Bob', age: 17}, {name: 'Sue', age: 21}, {name: 'Sam', age: 17}, ]; // Sort people by name people.sort( (p1, p2) => { if (p1.name < p2.name) return -1; if (p1.name > p2.name) return 1; return 0; });console.log(people.map(p => p.name)); // ['Ann', 'Bob', 'Gary', 'Sam', 'Sue'] // Re-sort people by age people.sort( (p1, p2) => { if (p1.age < p2.age) return -1; if (p1.age > p2.age) return 1; return 0; });console.log(people.map(p => p.name)); // 我們期望先按年齡,然後按年齡組中的姓名排序: // ['Bob', 'Sam', 'Ann', 'Gary', 'Sue'] // 但是我們可能會得到其中的任何一種,這取決於瀏覽器: // ['Sam', 'Bob', 'Ann', 'Gary', 'Sue'] // ['Bob', 'Sam', 'Gary', 'Ann', 'Sue'] // ['Sam', 'Bob', 'Gary', 'Ann', 'Sue']
如果你得到的是最後三個結果之一,則可能是你用的是 Google Chrome 瀏覽器,或者可能是沒有將 Array.sort()實現為「穩定」演算法的各種瀏覽器之一。
這是因為不同的 JS 引擎(在不同的瀏覽器上)採用了不同的路徑來實現排序,而且某些 JavaScript 引擎對短數組使用穩定的排序,而對長數組使用不穩定的排序。
這就導致了因為排序穩定性的行為不一致而引發了很多混亂。這就是為什麼在開發環境中與排序相關的內容似乎都可以工作,但是在生產環境中,由於和測試排序所使用的數組大小不同,我們開始看到其他內容的原因。
注意:有一些第三方庫,我強烈衷心推薦 Lodash,它能夠提供穩定的排序
但這些問題已經解決,我們在大多數瀏覽器上都能得到穩定的排序,同時語法保持不變。
由於本文有很多知識點和需要實際測試的功能,所以我們將在下一篇文章中繼續介紹更多的新功能。
原文:https://medium.com/@ideepak.jsd/javascript-new-features-part-1-f1a4360466