詳解JavaScript ES6~11新語法
前言
JavaScript是前端三劍客中的程式語言,獨佔前端市場
作者Brendan Eich只花了10天做出來的語言
關於他的歷史,可以參考阮一峰:Javascript诞生记
知道他的出生,就可以理解為何時至今日,仍有許多修修補補(es6~es11),讓js更能應付當今複雜的業務需求
多少開發者犧牲無數個夜晚,就是在這些坑裡打滾…
雖然現在已經有了TypeScript讓開發者可以避開js的許多缺陷
但最終仍是會回歸到JavaScript,亦可以在ts裡直接使用js原生各種語法
直至po文時的最新版本es11,可以當作是更多更好用的語法糖來輔助開發者撰寫程式
ECMAScript
而這些語法又是怎麼決定的呢?就是ECMAScript
ECMA規範是由包括瀏覽器廠商在內的各方組成,他們開會推動JavaScript提案,最終入選會有以下幾個階段:
- Stage 0 strawman:最初想法的提交
- Stage 1 proposal(提案):至少一名成員提供的正式提案文件,該文件包括API實例
- Stage 2 draft(草案):功能規範的初始版本,該版本包含功能規範的兩個實驗實現
- Stage 3 candidate(候選):提案規範通過審查並從廠商那裡收集回饋
- Stage 4 finished(完成):提案準備加入ECMAScript。要到瀏覽器或Node.Js,可能要更長的時間
ES6
1. CLASS
JavaScript是採用原型鏈的語言
早期都是透過原型鏈做出類似OO的概念
寫法相當複雜。在ES6終於推出了CLASS
class Animal { constructor(name, color) { this.name = name; this.color = color; } // 此為原型鏈上的屬性 toString() { console.log('name:' + this.name + ', color:' + this.color); } } var animal = new Animal('小黃', '黃色');//實例化 animal.toString(); // name: 小黃, color: 黃色 console.log(animal.hasOwnProperty('name')); //true console.log(animal.hasOwnProperty('toString')); // false console.log(animal.__proto__.hasOwnProperty('toString')); // true class Cat extends Animal { constructor(action) { // 子類必須在constructor中呼叫super函數,否則在new出來時會報錯 // 如果原本就沒寫constructor,預設自帶super的constructor會自動產生 super('cat','white'); this.action = action; } toString() { console.log(super.toString()); } } var cat = new Cat('catch') cat.toString(); console.log(cat instanceof Cat); // true console.log(cat instanceof Animal); // true
2. 模組(Module)
每個模組有自己的命名空間避免衝突,使用import
、export
來匯入匯出
基本上以一個.js
檔案視為一個模組
具體細節可以參考以前整理的筆記:[淺談JavaScript ES6的import與import{}及export及export default使用方式]
(https://blog.typeart.cc/%E6%B7%BA%E8%AB%87JavaScript%20ES6%E7%9A%84import%E8%88%87import%7B%7D%E5%8F%8Aexport%E5%8F%8Aexport%20default%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F/)
3. 箭頭函數 (arrow funciton)
() => {...}
,是function
的縮寫。最重要的是,他可以確保this
永遠指向自己
再也不需要寫var self = this
、var that = this
之類的東西了!
const add = (a, b) => { return a + b}; const res = add(1, 2); // 3 // 若語法簡單的話,可以省略`{}`與`return`。看起來會更加俐落 const minus = (a, b) => a - b; const res1 = minus(3, 1); // 2
4. 函式參數預設值
在函式若不傳入參數時,則使用預設值。更加精簡寫法
function example(height = 50, width = 40) { const newH = height * 10; const newW = width * 10; return newH + newW; } example(); // 900 (50*10 + 40*10)
5. 模板字串
長字串的組成,在以往都是透過+
來串接
其可讀性相當糟。有了模板字串,就會好閱讀許多
const firstName = 'Andy'; const lastName = 'Chou'; // 不使用模板字串 const name = 'Hello, My name is' + firstName + ', ' + lastName; // 使用模板字串 const nameWithLiteralString = `Hello, My name is ${firstName}, ${lastName}`;
甚至還可以做些簡單運算或函式呼叫
const example = `1 + 2 = ${1 + 2}` // 輸出:1 + 2 = 3 const arr = [1, 2, 3]; const exampleArr = `陣列資料: ${arr.join('-')}` // 陣列資料: 1-2-3
6. 解構賦值
讓JavaScript可以方便的從陣列、物件裡取得內容
const arr = [1, 2, 3, 4, 5]; const [one, two, three] = arr; console.log(one); // 1 console.log(two); // 2 console.log(three); // 3 // 若要跳過某些值 const [first,,,,last] = arr; console.log(first); // 1 console.log(last); // 5 // 物件也可以解構賦值 const student = { name: '小明', age: 18, city: 'Taipei' }; const {name, age, city} = student; console.log(name); // "小明" console.log(age); // "18" console.log(city); // "Taipei"
透過解構賦值,可以很方便的做資料交換
let a = 1; let b = 3; [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1
若無法從陣列取到值,可以設定預設值
let a, b; [a=3, b=4] = [1]; console.log(a); // 1 console.log(b); // 4
7. Spread operator
就是...
,可以將Array展開,若是Object時,則依key-value展開
const stuendts = ['Andy', 'Ben']; const people = ['Cara', ...stuendts, 'Daniel', 'Eason']; conslog.log(people); // ["Cara", "Andy", "Ben", "Daniel", "Eason"]
陣列的拷貝
const arr = [1, 2, 3]; const arr2 = [...arr]; // [1, 2, 3]。寫法等價於arr.slice() arr2.push(4); console.log(arr2); //[1, 2, 3, 4] console.log(arr); // [1, 2, 3] console.log(arr === arr2); // false
串接陣列
const arr = [1, 2, 3]; const arr2 = [4, 5]; const arr3 = [...arr, ...arr2]; // [1, 2, 3, 4, 5]; 寫法等價於 arr3 = arr.concat(arr2);
在es9時,可以做為物件的淺拷貝
const sourceObj = {a: 1, b: 2}; const copiedObj = {...sourceObj}; // {a: 1, b: 2} sourceObj === copiedObj; // false
es9也提供了串接物件
const obj1 = {a:1, b: 2}; const obj2 = {c:3, d: 4}; const obj3 = {...obj1, ...obj2}; // {a:1, b: 2, c:3, d: 4}
8. 物件(Object)屬性簡寫
若要組成物件的欄位名稱與前文中的變數一樣時,可以省略值。看起來更加精簡
const name = '小明', age = 18, city = '台北'; // ES6之前必須這樣寫 const customer = { name: name, age: age, city: city } // // {name: '小明', age: 18, city: '台北'} // es6後,可以直接這樣寫 const newCustomer = { name, age, city } // {name: '小明', age: 18, city: '台北'}
9. Promise
Promise是異步(非同步)寫法的一種解決方式,相比原本的callback寫法更加優雅
早期是開源社群的套件,後來納入語言標準
使用promise後,把callbackk hell壓平了
const waitSecond = new Promise((resolve, reject) => { setTimeout(resolve, 1000); }); waitSecond.then( () => { console.log('hello World after 1 second.'); // 1秒後輸出此行 return waitSecond; }).then( () => { console.log('Hell World after 2 sceond.'); // 2秒後輸出此行 })
而es8則再釋出更加完美的async, await
,直接把異步寫法變得像同步一樣
缺點就是,思緒落在複雜的業務邏輯時,有時會遺漏了await
,到runtime才發現錯誤XD
10. let 與 const 取代var
let: 一般變數,可以被蓋掉
const: 宣告後,就不能修改其內容。陣列、物件因為是指標,所以可以對其內容增減。但不得變更其指標
在早期,js的var作用域是全域的
也就是變數你在使用到之後才宣告,在執行時,會自動提到最上層,而到後面才賦值
也更容易會有污染問題
console.log(a); // 輸出undefined var a = 10;
使用let或const後,提早發現問題
console.log(a); // 拋出 ReferenceError: Cannot access 'a' before initialization let a = 10;
或著將變數限制在區塊內
{ var a = 10; } console.log(a); // 輸出10
使用let
{ let a = 10; } console.log(a); // 拋出 ReferenceError: a is not defined
const、let 就是要完全取代var。
沒有特殊操作的話,就別在使用var了
ES7 (2016)
1. Array.prototype.includes()
用來判斷陣列裡是否包含指定的值,若是的話回傳true;否則回傳false
跟以前indexOf的用法一樣,可以想成變成回傳boolean,在語意上更加明確
const arr = [1, 2, 3, 4, 5]; // 檢查陣列裡是否有3這個數字 arr.include(3); // true if (arr.include(3)) { ... } // ... 等同於以前indexOf的寫法 arr.indexOf(3); // 2 (回傳其陣列位置) // 要寫在if裡的話,則須加 `> -1`,語意上就不如ES7裡的include來得明確 if (arr.indexOf(3) > -1) { ... }
2. 指數操作
使用**
代表指數,等同於Math.pow()
語法
console.log(2**10); // 1024 // 等同於 console.log(Math.pow(2, 10)); // 1024
ES8 (2017)
1. 非同步寫法((異步寫法): async, await
台灣習慣叫非同步寫法,但我個人更喜歡大陸翻譯的異步寫法。覺得念起來較俐落
js最可怕的就是callback hell
,層層嵌套的callback寫法,著實讓人吃不消
寫完放置1個月,自己再回來讀,沒有註解幫忙的話,都不一定能理解自己再寫些什麼async
、await
則是直接彌平了callback hell
,看起來就像同步寫法
而用起來也相當簡單,也可以直接使用try-catch
捕捉錯誤
async test() { try { const result = await otherAsyncFunction(); console.log(result); // 輸出結果 } catch(e) { console.log(e); // 若otherAsyncFunction()拋錯,可以捕捉錯誤 } }
2. Object.values()
Object.values()
回傳的是Object自身屬性的所有值,不包括繼承來的值
const exampleObj = {a: 1, b: 2, c: 3, d:4}; console.log(Object.value(exampleObj)); // [1, 2, 3, 4]; // 若以前要做到同樣的事情,須用下列寫法。冗長許多 const values = Object.keys(exampleObj).map(key => exampleObj[key]);
3. Object.entries()
Object.entries()
回傳傳入物件自身可列舉的key, value
const exampleObj = {a: 1, b: 2, c: 3, d:4}; console.log(Object.entries(exampleObj)); // [["a", 1], ["b", 2], ["c", 3], ["d", 4]]; // 通常會跟for搭配使用 for (const [key, value] of Object.entries(exampleObj)) { console.log(`key: ${key}, value: ${value}`); } // key: a, value: 1 // key: b, value: 2 // key: c, value: 3 // key: d, value: 4
4. String padStart() 和 padEnd()
可以將字串開頭或結尾增加其他內容,並填到指定長度
在之前這些功能通常是引入萬用輔助套件(如lodash)會一併擁有
現在原生語法直接提供
String.padStart(填充長度, 要填充的內容); // 如果要填充的內容太多,超過「填充長度」,則從最左邊開始填到長度上限,多餘的部份將被截斷
最常使用的情況應該就是金額了吧,填到指定長度,不足補0
// padStart '100'.padStart(5, 0); // 00100 // 如果要填充的內容超過「填充長度」。則從最左邊開始填到長度上限 '100'.padStart(5, '987'); // 98100 // padEnd '100'.padEnd(5, 9); // 10099 // 如果要填充的內容超過「填充長度」。則從最左邊開始填到長度上限 '100'.padStart(5, '987'); // 10098
5. 函數參數列表结尾允许逗號
主要是方便git版控降低不必要的行數變更
以Angular為例
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], // <--這裡的最後一個「,」 }) export class AppComponent { }
6. Object.getOwnPropertyDescriptors()
取得自身的Descriptor。一般開發業務需求通常不太會用到
const exampleObj = {a: 1, b: 2, c: 3, d:4}; Object.getOwnPropertyDescriptors(exampleObj); // {a: {…}, b: {…}, c: {…}, d: {…}} // 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} // d: {value: 4, writable: true, enumerable: true, configurable: true} // __proto__: Object
7. SharedArrayBuffer 物件
SharedArrayBuffer 是一固定長度的原始二進位資料緩沖區,類似於 ArrayBuffer
都可以用來在shared memory 上建立資料。與 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分離
/** * @param length 大小,單位是byte * @returns {SharedArrayBuffer} 其内容初始化為0。 */ const sab = new SharedArrayBuffer(length);
8. Atomics 物件
Atomics 物件,提供了一组靜態方法對 SharedArrayBuffer 進行原子操作
Atomics 所有屬性與函式都是靜態的,就像Math
一樣。不能new
出來
若是multi-thread同時讀寫同一位置的資料,原子操作保證正在操作的資料符合預期:即一定會在前一個前子操作結束後才會進行下一個。操作過程不會被中斷
可以說是因應Node.Js 開發 multi-thread Server 而補強的功能,在前端開發上用到機會頗低
chrome已經提供支援
ES9 (2018)
1. 在迴圈中使用await
在async function裡,有時會需要在同步寫法的for-loop使用異步(非同步)函式
async function process(array) { for (const i of array) { await doSomething(i); } } async function process(array) { array.forEach(async i => { await doSomething(i); }); }
以上程式碼不會如期望的輸出想要的結果
for-loop本身仍是同步,會在迴圈內的異步函式完成前,執行完整個for迴圈,接著才把裡面的異步函式逐一執行
ES9增加了asynchronous iterators,讓await可以和for-loop一起使用,逐步執行異步操作
async function process(array) { for await (const i of array) { doSomething(i); } }
2. Promise.finally()
Promise不論成功(.then()
)或失敗(.catch()
),都會再往後執行的部份
function process() { process1() .then(process2) .then(process3) .catch(err => { console.log(err); }) .finally(() => { console.log(`不論前面成功或失敗,此處一定會執行!`); }); }
3. Rest、Spread 不定長度參數
在ES2015引入了Rest不定長度參數 ...
,可以將不定長度的參數轉成陣列傳入
function restParams(p1, p2, ...p3) { console.log(p1); // 1 console.log(p2); // 2 console.log(p3); // [3, 4, 5] } restParams(1, 2, 3, 4, 5);
而spread則是與rest相反,將陣列轉換成單獨參數
如Math.max()回傳傳入數字中的最大值
const values = [19, 90, -2, 6, 25]; console.log( Math.max(...values) ); // 90
也為Object提供解構賦值的功能
const myObject = { a: 1, b: 2, c: 3 }; const { a, ...r } = myObject; // a = 1 // r = { b: 2, c: 3 } // 亦可以用在function入參裡 function restObjectInParam({ a, ...r }) { console.log(a); // 1 console.log(r); // {b: 2, c: 3} } restObjectInParam({ a: 1, b: 2, c: 3 });
與陣列一樣,spread參數只能在宣告的结尾處使用
spread參數可以在其他object使用
const obj1 = { a: 1, b: 2, c: 3 }; const obj2 = { ...obj1, d: 4 }; // obj2 = { a: 1, b: 2, c: 3, d: 4 }
spread可以做為object的淺拷貝。像是obj2 = { ...obj1 }
4. 正則表達式(RegExp)分組命名
RegExp可以回傳匹配的分組
const regExpDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/; const match = regExpDate.exec('2020-06-25'); const year = match[1]; // 2020 const month = match[2]; // 06 const day = match[3]; // 25
但這樣較難閱讀
在ES9裡則提供在(
前使用?<name>
來命名分組
const regExpDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/; const match = regExpDate.exec('2020-06-25'); const year = match.groups.year; // 2020 const month = match.groups.month; // 06 const day = match.groups.day; // 25 // 以上任意若匹配失敗,則回傳undefined
也可以使用在replace()
。如將日期轉換成美國的mm-dd-yyyy格式
const regExpDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/; const d = '2020-06-25'; const usDate = d.replace(regExpDate, '$<month>-$<day>-$<year>'); // 06-25-2020
5. 正則表達式(RegExp)反向斷言
JavaScript原本是先行斷言(lookahead),代表會成功匹配,但不會將他納入結果
const reLookahead = /\D(?=\d+)/; const match = reLookahead.exec('$123.45'); console.log(match[0]); // $ console.log(match[1]); // undefiled。不會納入後面的數字結果
es9增加反向斷言,就可以忽略前方符號,只單純取得後面數字
const reLookbehind = /(?<=\D)\d+/; const match = reLookbehind.exec('$123.45'); console.log(match[0]); // 123
以上皆是肯定斷言。前面寫的非數字\D
必須存在,否則會匹配失敗
有肯定就會有否定,也存在著否定反向斷言,表示一個值必須不存在
const reLookbehindNegative = /(?<!\D)\d+/; const match = reLookbehindNegative.exec('$123.45'); console.log(match[0]); // 23
6. 正則表達式(RegExp) dotAll模式
.
代表匹配除了enter的任何符號,加上s
flag後,允許匹配enter
/hello.world/.test('hello\nworld'); // false /hello.world/s.test('hello\nworld'); // true
7. 正則表達式(RegExp) Unicode 轉義
格式為\p{...}
、\P{...}
,並在RegExp中增加u
flag。p
的區塊內可以以key-value的方式設定要匹配的屬性,不一定是具體內容
const reGreekSymbol = /\p{Script=Greek}/u; reGreekSymbol.test('π'); // true
可以避免使用特定Unicode區間來判斷,提升可讀性
ES10 (2019)
1. 行分隔符(U + 2028)和段分隔符(U + 2029)現在允許存在字串文字裡,與json匹配
以前會視為終止符號,導致SyntaxError
2. 更加友好的 JSON.stringify
如果輸入 Unicode 格式但超出範圍,原先JSON.stringify會回傳格式錯誤的的Unicode字串。
現在第3階段提案,使其成為有效的Unicode,並以UTF-8呈現
3. Array增加的flat()
和flatMap()
flat() 壓平陣列
const arr1 = [1, 2, [3, 4]]; arr1.flat(); // [1, 2, 3, 4] const arr2 = [1, 2, [3, 4, [5, 6]]]; arr2.flat(); // [1, 2, 3, 4, [5, 6]] // 在flat 傳入數字,代表壓平深度 arr2.flat(2); // [1, 2, 3, 4, 5, 6]
flatMap(),相當於reduce + concat,可以壓平一個維度
let arr = ["早安", "", "今天天氣不錯"] arr.map(s => s.split("")) // [["早", "安"], [""], ["今", "天", "天", "氣", "不", "錯"]] arr.flatMap(s => s.split('')); // ["早", "安", "", "今", "天", "天", "氣", "不", "錯"]
4. String增加trimStart()
和trimEnd()
字串去頭或去尾空白
5. Object.fromEntries()
Object.fromEntries()
是 Object.entries()
的反轉
透過Object.fromEntries()
,可以將Map或Array轉成Object
// 將 Map 轉成 object const map = new Map([ ['a', 'b'], ['c', 25] ]); // Map(2) {"a" => "b", "c" => 25} const obj = Object.fromEntries(map); console.log(obj); // { a: "b", c: 25 } // 將 Array 轉成 object const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]; const obj = Object.fromEntries(arr); console.log(obj); // { 0: "a", 1: "b", 2: "c" }
6. Symbol.prototype.description
若不熟悉Symbol,這篇寫的還不錯:ES6 Symbol 到底有什么用?
以前要得到Symbol的字串型式,需再轉型成字串類型
const sym = Symbol('The description'); String(sym) === 'Symbol(The description)'; // true sym.toString() === 'Symbol(The description)'; // true
現在則是提供使用description
可以直接取得
sym.description === 'The description'; // true
7. String.prototype.matchAll
matchAll
會得到匹配正則表達式分組結果的迭代器。傳入的正則表達式需有g
flag
可以直接透過使用for-of
loop 或 Array.from()
逐一取得資料
const regexp = /foo*/g; const str = 'baseball, football, foot'; const matches = str.matchAll(regexp); for (const match of matches) { console.log(match); } // 或 Array.from(matches, m => m); // [Array(1), Array(1)] // 0: ["foo", index: 10, input: "baseball, football, foot", groups: undefined] // 1: ["foo", index: 20, input: "baseball, football, foot", groups: undefined]
8.Function.prototype.toString()
回傳精確說明,包含空格、註解
function /* 註解 */ foo /* 第二個 註解 */() {} // 之前不會輸出註解 console.log(foo.toString()); // function foo(){} // es2019 後,則會輸出註解 console.log(foo.toString()); // function /* 註解 */ foo /* 第二個 註解 */() {} // arrow fuction亦同 const arrow = /* 註解 */ () /* 第二個 註解 */ => {}; console.log(arrow.toString()); // () /* 第二個 註解 */ => {} // 注意,只有輸出`() =>`` 之間的註解,`()`前面的註解並不會輸出唷!
9. 修改 catch
綁定
在之前使用catch
,不論是否有用到,都一定要傳入一個e
參數代表接到的錯誤
現在則若用不到,可以省略不傳
try {...} catch(e) {...} // 若用不到e,可以省略不傳 try {...} catch {...}
10. 新的基本數字類型:BigInt
ES5 之前的基本類型有5種:String、Number、Boolean、Null、Undefined
ES6 增加:Symbol
,6種
ES10 再增加:BigInt
,達到7種
BigInt可以用任意精度表示整數
使用BigInt可以安全的儲存、操作大整數。以避免這個數字超過Number能夠表示的安全整數範圍
具體細節可以參考BigInt:JavaScript 中的任意精度整数
ES11 (2020)
1. Promise.allSettled
Promise.all
可以讓多個promise都處理完後再進行到下一步
但最大的問題:其中一個失敗時,就會全部進入catch區塊
無法針對部份成功繼續處理allSettled
就是為了解決這個問題而新出來的語法
Promise.all([ Promise.reject({code: 500, msg: '內部錯誤'}), Promise.resolve({ code: 200, list: [1, 2, 3]}), Promise.resolve({code: 200, list: ['a', 'b', 'c']}) ]).then((res) => { // 若其中一個reject,則不會進入 renderData(res); }) .catch((error) => { // 會直接到catch區塊 showErrorMessage({code: 500, msg: '內部錯誤'}); })
Promise.allSettled([ Promise.reject({code: 500, msg: '內部錯誤'}), Promise.resolve({ code: 200, list: [1, 2, 3]}), Promise.resolve({code: 200, list: ['a', 'b', 'c']}) ]) .then((res) => { /* 0: {status: "rejected", reason: {…}} 1: {status: "fulfilled", value: {…}} 2: {status: "fulfilled", value: {…}} */ // 部份失敗時,仍會進來。再手動排除取得失敗的資料 renderData(res.filter(r => r.status !== 'rejected')); });
2. 可選鏈(Optional chaining) ?
在開發中,很容易遇到檢查資料是否存在而在前面先寫if判斷
const isUserExist = user && user.info; if (isUserExist) { username = user.info.name; }
若回來的資料是null或著user物件底下沒有.info
,就會拋Uncaught TypeError: Cannot read property...
這錯誤
導致程式無法繼續執行
有了?
後,語法就精簡多了
const username = user?.info?.name;
若存在,則得到name的值,不存在,賦予undefined
搭配||
使用,只要一行搞定!
const username = user?.name || 'guest';
3. 空值合併運算(Nullish coalescing Operator) ??
在js中,碰到0
、null
、undefined
,會自動轉型成false
但有時0其實是正常值,只能容錯到undefined
跟null
/** * user = { * level: 0 * } */ const level = user.level || '查無等級'; // 會變成「查無等級」,而不是期望的0 // 為了解決這問題,必須展開使用 if 簡單式處理 const level = user.level !== undefined && user.level !== null ? user.level : '查無等級';
但有了??
後,就可以保持精簡的寫法
const username = user.level ?? '查無等級'; // 0 。若level值不存在,則變成「查無等級」
4. dynamic-import 動態載入
就是字面上的意思,應該很容易理解。就是有需要時再載入相關邏輯
el.onclick = () => { import(`/js/current-logic.js`) .then((module) => { module.doSomthing(); }) .catch((err) => { handleError(err); }) }
5. globalThis
JavaScript在不同環境對全局有不同的方式
有用Node.Js 開發 Server 程式的話應該都知道
在Node.Js中透過global,web則是Window, this 等。
而js最大毛病就是this複雜的指向,會依賴上下文而有所不同
在以前的作法是
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/globalThis const getGlobal = function () { if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } throw new Error('unable to locate global object'); }; var globals = getGlobal();
而ES2020提供了globalThis
,提供了統一的方式來獲得全局對象
參考資料
掘金 / 种草 ES2020 新特性
掘金 / ES6、ES7、ES8、ES9、ES10新特性一览
ES6 Symbol 到底有什么用?
知乎 / BigInt:JavaScript 中的任意精度整数