自從ECMAScript在2015年正式發布了ES6起,到現在已經有了8個年頭了,而自從2015年起,每年的6月都會發布一個新的版本,以當時的年作為版本號。
在這眾多的版本中已經出現了許多新特性,為了方便記憶,我將所有的新特性全部整理在這篇文章中。
ps:有的資料稱ES2015後的所有版本統稱為ES6,也有的稱ES6表示ES2015,ES7表示ES2016以此類推,這裡不做討論。
ES2015是改動最大的一個版本,基本上對ES2015之前的所有的內容都做了擴展,大體如下圖所示:
在ES6之前只有一種宣告變數的方式,就是使用var
關鍵字,在ES2015中新增了let
和const
關鍵字來宣告變數與常數,
程式碼如下:
// 宣告變數let v = 100 v = 200 // 宣告常數const V = 200 // 修改常數// V = 300 // 報錯
同時使用let
和const
關鍵字宣告的變數或常數是具有區塊級作用域的,
範例程式碼如下:
{ var v = 100 } { let val = 200 } console.log(v) console.log(val) // 報錯val is not defined
值得注意的是使用let
或const
關鍵字宣告的變數不具有變數提升的特性,且有暫時性死區的特性。
在ES2015中允許函數使用預設值,範例程式碼如下:
// es2015之前function foo(v) { v = v ? v : 100 return v } // es2015 function bar(v = 100) { return v }
值得注意的是如果有多個參數時,預設參數必須從後向前使用。
在ES2015中新增了箭頭函數,這是函數的一種簡寫形式,範例程式碼如下:
function foo(v) { return v`` } // 箭頭函數寫法 const foo = (v) => { return v } // 簡寫形式1 const foo = v => { // 只有一個參數可以省略括號return v } // 簡寫形式2 const foo = v => v // 語句中只有return時可以省略return和花括號
值得注意的是箭頭函數的this
是根據執行上下文決定的,內部並不會綁定this
。
使用箭頭函數時內部並不存在arguments對象,而是採用剩餘參數的方式代替,
範例程式碼如下:
const foo = (...args) => { // console.log(arguments) // ReferenceError: arguments is not defined console.log(args) // args 是一個陣列} foo(1, 2, 3, 4) // [ 1, 2, 3, 4 ]
在ES2015中為函數增加的要給name屬性,該屬性指向函數的名稱,
範例程式碼如下:
function foo(v) { return v } const bar = v => v console.log(foo.name) // foo console.log(bar.name) // bar
在ES2015中對數值的擴展主要時為Math
和Number
兩個物件增加一些方法,以及二進位和八進位的表示方法。
在ES2015中使用0b
或0B
表示二進制,使用0o
或0O
表示八進位。
範例程式碼如下:
console.log(0b111111111 === 511) // true console.log(0o777 === 511) // true
為Number擴充的屬性與方法如下:
屬性/方法名稱 | 描述 |
---|---|
Number.EPSILON | 數值最小精確度 |
Number.MIN_SAFE_INTEGER | 最小安全數( -2^53 ) |
Number.MAX_SAFE_INTEGER | 最大安全數( 2^53 ) |
Number.parseInt() | 把參數解析為整數並回傳 |
Number.parseFloat() | 把參數解析為浮點數並回傳 |
Number.isFinite() | 判斷是否為有限數值 |
Number.isNaN() | 判斷是否為NaN |
Number.isInteger() | 判斷是否為整數 |
Number.isSafeInteger() | 判斷數值是否在安全範圍內 |
為Math擴充的方法如下:
方法名稱 | 描述 |
---|---|
Math.trunc() | 傳回數值整數部分 |
Math.sign() | 傳回數值類型(正数1、负数-1、零0 ) |
ES2015引入模板字串,使用反引號(`)定義,模板字串會保留格式,且可以使用變量,
範例程式碼如下:
// 使用` 定義模板字串let str = `一碗週` // 範本字串可以保留格式let str2 = `一碗週` // 模板字串可以使用變數const myName = '一碗週' let str3 = `author: ${myName}` // 使用${} 進行包裹
ES2015也為String和String的實例擴展了一些方法,如下:
方法名稱 | 描述 |
---|---|
String.fromCodePoint() | 用於從Unicode 碼點傳回對應字元 |
String.raw() | 傳回一個斜線都被轉義(即斜線前面再加一個斜線)的字串,往往用於模板字串的處理方法。 |
String.prototype.codePointAt() | 傳回字元對應碼點(String.fromCodePoint()的逆操作) |
String.prototype.normalize() | 把字元的不同表示方法統一為同樣形式,傳回新字串(Unicode正規化) |
String .prototype.repeat() | 把字串重複n次,回傳處理後的字串 |
String.prototype.includes() | 判斷是否存在指定字串 |
String.prototype.startsWith() | 判斷字串是否存在原始字串的頭部 |
String.prototype.endsWith() | 判斷字串是否存在原始字串的尾部 |
在ES2015中提供了展開運算符,即...,在數組中使用可以將數組展開,並以逗號分隔,
示例代碼如下:
const arr = [1, 2, 3, 4, 5, 6] const newArr = [...arr] // 複製數組console.log(Math.max.call(null, ...arr)) // 將數組中的每一項作為參數使用
除此之外,還為Array以及陣列提供了一系列方法,來逐一介紹:
Array.from()
:將類別數組物件或可迭代物件建立為新的陣列,範例程式碼如下:
function foo() { return Array.from(arguments) // 將arguments 轉換為陣列} console.log(foo(1, 2, 3, 4, 5, 6)) // [ 1, 2, 3, 4, 5, 6 ]
Array.of()
:建立一個具有可變數量參數的新數組實例,範例程式碼如下:
Array.of(1) // [1] Array.of(true, 1, '一碗週') // [true, 1, '一碗週']
Array.prototype.copyWithin(),淺複製數組的一部分到同一數組中的另一個位置,並返回它,不會改變原始數組的長度。
範例程式碼如下:
const arr = [1, 2, 3, 4] // 從索引2 開始,到結束將內容複製到索引0 的位置arr.copyWithin(0, 2) // [ 3, 4, 3, 4 ]
Array.prototype.find()
,根據給定的回呼函數,找到符合的第一個元素,找不到回傳undefined ,範例程式碼如下:
const arr = [1, 2, 3, 4] arr.find(item => item === 2) // 2(表示元素)、
Array.prototype.findIndex()
,根據給定的回呼函數,找到匹配的第一個元素的索引,找不到返回-1 ,範例程式碼如下:
const arr = [1, 2, 3, 4] arr.findIndex(item => item === 2) // 1 (表示索引)
Array.prototype.fill()
,將給定值填入數組,範例程式碼如下:
const arr = [1, 2, 3, 4 ] // 將給定值填入索引1-3 arr.fill('一碗週', 1, 3) // [ 1, '一碗週', '一碗週', 4 ]
Array.prototype.keys()
,傳回一個可迭代的對象,其內容為陣列的key ,範例程式碼如下:
const arr = [1, true, '一碗週'] const keys = arr.keys() for (const i of keys) { console.log(i) // 遍歷結果0 1 2 }
Array.prototype.values()
,傳回一個可迭代的對象,其內容為數組的value ,
範例程式碼如下:
const arr = [1, true, '一碗週'] const values = arr.values() for (const i of values) { console.log(i) // 遍歷結果1 true 一碗週}
Array.prototype.entries()
,傳回一個可迭代的對象,其內容是一個數組,索引0
為原數組的元素, 1
為原始數組該位置的值,
範例程式碼如下:
const arr = [1, true, '一碗週'] const iterator = arr.entries() console.log(Array.from(iterator)) // [ [ 0, 1 ], [ 1, true ], [ 2, '一碗週' ] ]
ES2015中允許物件的屬性名稱和屬性值一致時可以只寫屬性名,
範例程式碼如下:
const myName = '一碗週' const age = 18 const person = { myName, age } console.log(person) // { myName: '一碗週', age: 18 }
還有就是在定義物件時,允許使用[]包裹表達式作為屬性名,範例程式碼如下:
const myName = '一碗週' const age = 18 const person = { myName, ['a' + 'g' + 'e']: age, } console.log(person) // { myName: '一碗週', age: 18 }
Object.is()
:用來比較兩個值是否相等,用於解NaN ≠= NaN,+0 === - 0的問題,
範例程式碼如下:
console.log(NaN === NaN) // false console.log(+0 === -0) // true console.log(Object.is(NaN, NaN)) // true console.log(Object.is(+0, -0)) // false
Object.assign()
:將所有可枚舉屬性的值從一個或多個來源對象複製到目標對象,並返回目標對象,
範例程式碼如下:
const person = Object.assign({}, { name: '一碗週' }, { age: 18 }) console.log(person) // { name: '一碗週', age: 18 }
Object.getPrototypeOf()
:取得原型物件;Object.setPrototypeOf()
:設定原型物件。在ES2015中提出了類別的概念,在語法的層面上有了類,範例程式碼如下:
class Person { constructor(age) { // 屬性this.myName = '一碗週' this.age = age } // 靜態方法static print() { console.log() } // 存取器get myName() { console.log('getter') return '一碗週' } set myName(v) { console.log('setter' + v) } setName(v) { this.myName = v } } const person = new Person(18) person.setName('ywanzhou') // 觸發setter 存取器console.log(person.myName) // 觸發getter 存取器
在ES2015中提出ESModel模組化規範,這是第一個官方層面的模組化規範,在這個規範中允許我們使用export導出模組,使用import引入模組,
範例程式碼如下:
import a from 'm' // 導入模組m 中的預設導出,將其命名為a import a, { b } from 'm' // 導入模組m 中的預設導出以及單獨導入成員b import * as A from 'm' // 導入模組中的所有成員import 'm' // 執行m 模組export const b = 1 // 單獨導出export default b // 預設導出export { b } // 按需導出export { b as bb } // 改名導出export { b } from 'm' // 導入模組m 中的成員b 並導出
ES2015新增了解構賦值的語法,允許我們使用按照一定的模式,在數組或物件中提取指定的值,
範例程式碼如下:
// 陣列的解構賦值let [name, age, hobby = 'coding' /* 結構賦值的預設值*/] = ['一碗週', 18] // 交換兩個變數的值let a = 1 let b = 2 ;[a, b] = [b, a] console.log(a, b) // 2 1 // 物件的結構賦值let { name: ObjName /* 解構賦值重新命名*/, sex } = { name: '一碗週', sex: 1 } // 函數參數的解構賦值function bar({ name, age }) { return name + age } bar({ name: '一碗週', age: 18 }) // 一碗週18
Symbol是ES2015中新增的一種資料類型,透過Symbol()
方法創建,可以傳遞一個字串作為參數,用於描述該Symbol;
透過Symbol()方法所建立的symbol值都是唯一的,範例程式碼如下:
/** * 語法* Symbol([description]) * * description -> 是一個可選的描述資訊*/ // 建立一個Symbol 類型的值const mySymbol = Symbol() console.log(mySymbol) // Symbol() const myName = Symbol('一碗週') console.log(typeof myName) // symbol
Symbol還有一系列屬性和方法這裡就不作介紹了。
Promise是ES2015中提供的一個非同步解決方案,解決了回調地獄的問題。
透過Promise()
建構函式可以建立promise對象,每個Promise物件都有以下幾個狀態:
狀態的切換只有兩種,分別是:
一旦狀態改變,就不會再次改變
Promise
實例中存在要給then
方法,允許我們在Promise
實例中鍊式調用,每個then
方法還會傳回一個Promise
實例,
如下圖所示:
範例程式碼如下:
new Promise((resolve, reject) => { console.log('我是第一個Promise中的標誌') resolve() }) .then(() => { console.log('我是第一個then中的標誌') }) .then(() => { console.log('我是第二個then中的log,但是我出現了異常') throw new Error('Error') }) .then(() => { console.log('我是第三個then中的第一個回調的log,但是我不會執行,因為我上面出現了異常') }, () => { console.log('我是第三個then中的第二個回呼的log,我執行了') }) .then(() => { console.log('我是第四個then中的log,我可以正常執行') }) /* 執行結果如下我是第一個Promise中的log 我是第一個then中的log 我是第二個then中的log,但是我出現了異常我是第三個then中的第二個回調的log,我執行了我是第四個then中的log,我可以正常執行*/
相關Promise的一些方法如下:
Promise.prototype.then()
:它最多需要有兩個參數:Promise的成功和失敗情況的回調函數;Promise.prototype.catch()
:等於then
方法的第二個參數;Promise.all()
:將多個實例包裝成一個新實例,傳回全部實例狀態變更後的結果數組(齊變更再返回)Promise.race()
:將多個實例包裝成一個新實例,傳回全部Promise.resolve()
:將物件轉為Promise物件(等價於new Promise(resolve => resolve())
)Promise.reject()
:將物件轉為狀態為rejected
的Promise物件(等價於new Promise((resolve, reject) => reject())
)Iterator即迭代器,它是一種接口,為各種不同的資料結構提供了統一的存取機制,換句話說,只要有任何資料結構部署了迭代接口,就可以使用統一的方式的來遍歷它。
實作可迭代介面的資料結構,一般都本身實作或繼承了以Symbol.iterator
屬性的,就屬於可迭代物件。 Symbol.iterator
屬性本身就是一個函數,就是目前資料結構預設的遍歷器產生函數。
一個包含next()
方法的對象,才可以稱為一個迭代對象。 next()
物件的會有傳回一個對象,而物件中包含兩個值,
如下所示:
value
:迭代器傳回的任何JavaScript
值。 done
為true
時可省略。done
:一個布林值,為false
時表示迭代未停止,為true
時立即停止迭代器,且可以省略value
的值。JavaScript原生提供的迭代器介面如下圖所示:
現在我們為obj來實作一個迭代器,程式碼如下:
const obj = { [Symbol.iterator] () { return { next () { console.log('迭代器執行了'); return { value: '', done: true // 標誌是否結束,true表示已經結束} } } } }
我們在next()
方法中加入了一個列印,為了驗證迭代器執行了,最終的運行結果為
迭代器執行了
Generator是ES2015中提供的一種非同步程式解決方案,定義Generator函數在function
關鍵字和函數名稱中間使用*
星號,函數內部使用yield
關鍵字定義不同的狀態。
範例程式碼如下:
function* testGenerator() { // yield定義一個狀態yield '一碗週' yield 'es新特性' return 'generator' // 終結Generator,後面即使有yield關鍵字也無效} const g = testGenerator() // 傳回Generator 對象,透過next()方法移動狀態g.next() /* { value: '一碗週', done: false } */ g.next() /* { value: 'es新特性', done: false } */ g.next() /* { value: 'generator', done: true } */
Proxy物件用於建立一個代理對象,從而實現基本操作的攔截和自定義,基本操作包含13種,如下表所示:
攔截⽅法 | 觸發⽅式 |
---|---|
get(target, propKey, receiver) | 讀取某個屬性 |
set(target, propKey, value, receiver) | 寫⼊某個屬性 |
has(target, propKey) | in運算子 |
deleteProperty(target, propKey) | delete運算符 |
getPrototypeOf(target) | Object.getPropertypeOf() |
setPrototypeOf | |
target | , |
proto) | Object.setPrototypeOf() |
isExtensible(target) | Object.isExtensible() |
preventExtensions(Propertytarget) | Object.preventExtensions() | 0nidsions(Descriptnd, Propertyd, nex;
(target, propKey, propDesc) | Object.defineProperty() |
ownKeys(target) | Object.keys() 、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols() |
apply(target, thisArg, args) | 調⽤⼀個函數 |
construct(target, args) | ⽤ new 調⽤⼀個函數 |
Vue3就是基於Proxy
進行編寫的,下面這段程式碼展示了Proxy
物件的使用:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/css/bootstrap.min.css" rel="external nofollow" rel="stylesheet" /> <title>透過set自動更新dom</title> </head> <body> <p class="card" style="width: 300px; margin: 100px auto"> <p class="card-body"> <h1 id="name"></h1> <button id="btn" class="btn btn-primary">修改</button> </p> </p> <script> // 取得DOM節點const name = document.getElementById('name') const btn = document.getElementById('btn') // 定義一個修改值的函數const updateDOM = (el, value) => { el.innerHTML = value } const person = new Proxy({ 名: '一碗粥', }, { set(target, propKey, value) { // 如果裡面的值改變就去呼叫我們的updateDOM updateDOM(name, value) target[propKey] = value return true }, }) name.innerHTML = person.name // 點選按鈕觸發修改動作btn.addEventListener('click', () => { person.name === '一碗週' ? (person.name = '一碗粥') : (person.name = '一碗週') }) </script> </body> </html>
上面的程式碼就利用set方法進行資料綁定,如果物件改變,就自動更新我們的DOM。
Reflect是ECMAScript2015提供的對象,它提供了一些攔截JavaScript操作的靜態方法,這些方法與Proxy中的handlers
中的方法一致。
Reflect並不是建構函數,也就是說它不能夠被實例化。
Proxy
物件中的每一個攔截操作(例如: get
、 delete
等),內部都對應的呼叫了Reflect
的方法。它所提供的靜態方法與Proxy中的handlers
中的方法名稱都一致,
具體如下:
預設調⽤ | 功能 |
---|---|
Reflect.get() | 取得物件身上某個屬性的值 |
Reflect.set() | 在物件上設定屬性 |
Reflect.has () | 判斷一個物件是否存在某個屬性 |
Reflect.deleteProperty() | 刪除物件上的屬性 |
Reflect.getPrototypeOf() | 取得指定物件原型的函數 |
Reflect.setPrototypeOf() | 設定或改變物件原型的函數 |
Reflect.isExtensible() | 判斷一個物件是否可擴充(即是否能夠新增新的屬性) |
Reflect.preventExtensions() | 阻止新屬性新增至物件 |
Reflect.getOwnPropertyDescriptor() | 取得給定屬性的屬性描述符 |
Reflect.defineProperty() | 定義或修改一個物件的屬性 |
Reflect .ownKeys() | 傳回由目標物件本身的屬性鍵組成的陣列 |
Reflect.apply() | 對一個函式進行呼叫操作,同時可以傳入一個陣列作為呼叫參數 |
Reflect.construct() | 對建構函式進行new操作,實作創建類別的實例 |
Set
、 Map
、 WeakSet
、 WeakMap
是ES2015中新增的幾個物件:
Set
和WeakSet
與陣列類似,準確的它他們是集合,這兩者的差別就是Set
可以儲存任何資料類型,而WeakSet
只能儲存物件的引用,而且是弱引用;Set物件在實際開發中最常見的就是實現資料去重,範例程式碼如下:
const arr = [1, 2, 2, 3, 4 , 3, 5] const set = new Set(arr) // set物件可以使用... 展開所有項目console.log([...set]) // [ 1, 2, 3, 4, 5 ]
Map
和WeakMap
與物件類似,儲存方式是鍵值對形式的,這兩者的區別Map
的鍵值對都是可以是任意的而WeakMap
鍵必須是物件的引用而值可以是任意類型的。ES2016發布的新特性比較少,主要就兩個新特性,如下圖:
ES2016中新增指數**
,也叫冪運算符,與Math.pow()有著相同的功能,
範例程式碼如下:
console.log(2 ** 10 === Math.pow(2, 10 )) // true
在ES2016中在陣列原型上增加了includes()
方法,該方法用於判斷一個數組中是否包含指定的值,傳回一個布林值,
範例程式碼如下:
const arr = [1, 2, 3, 4, 5, NaN] console.log(arr.indexOf(NaN)) // -1 console.log(arr.includes(NaN)) // true
值得注意的是使用includes()
時NaN
與NaN
、 +0
與-0
是相等的。
Promise的出現雖然解決了回調地獄的問題,但是如果鍊式調用特別多的話可讀性還是會變差,在ES2017中新增了async/await語法糖解決了這個問題。
Promise的寫法如下:
;(function () { function promise(v) { return new Promise((resolve, reject) => { resolve(v) }) } const p = promise(1) p.then(res => { return promise(res) }).then(res => { console.log(res) }) })()
如果下一個Promise依賴上一個,這種鍊式呼叫就會非常的長,現在我們用async/await語法糖改寫一下:
;(async function () { function promise(v) { return new Promise((resolve, reject) => { resolve(v) }) } const r1 = await promise(1) const r2 = await promise(r1) const res = await promise(r2) console.log(res) })()
可以看到,我們可以用async/await語法糖將Promise改寫為平級的寫法。
ES2017中新增了Atomics對象,該對象提供了一系列靜態方法用於操作SharedArrayBuffer和ArrayBuffer對象,該對象並不能使用new
關鍵字進行實例化,僅提供了一些靜態的屬性和方法對象
在在ES2017中為Object擴展了三個靜態方法,如下所示:
Object.values()
:傳回一個給定物件本身的所有可枚舉屬性值的陣列;Object.entries()
:傳回一個給定物件本身可枚舉屬性的鍵值對數組;Object.getOwnPropertyDescriptors()
:傳回給定物件所有自有屬性的屬性描述符。在ES2017中允許我們在函數參數列表的最後面加上逗號,這個小特性非常的有用,因為為尾逗號的更新的時候僅僅需要改動一行程式碼,如果不適用尾逗號則是改動兩行程式碼。
實例程式碼如下:
function fun( aaaaa, bbbbb, ccccc, ) {}
如果有尾逗號,只需要在最後面加上一行就好;如果不存在則需要在後面加上要給逗號,然後在加一行。這在版本管理中就改動了兩行,而不是一行。
在ES2017中為字串新增了兩個實例方法,分別是:
padStart()
:在字串開頭填滿空格;padEnd()
:在字串結尾填入空格;範例程式碼如下:
const str = '一碗週' console.log(str.padStart(10)) /* 一碗週*/ console.log(str.padEnd(10)) /* 一碗週*/
在ES2018中新增了for await...of
語句,該用於可以遍歷異步可迭代對象,
範例程式碼如下:
var asyncIterable = { [Symbol.asyncIterator]() { return { i: 0, next() { if (this.i < 3) { return Promise.resolve({ value: this.i++, done: false }) } return Promise.resolve({ done: true }) }, } }, } ;(async function () { for await (num of asyncIterable) { console.log(num) } })() // 0 // 1 // 2
在ES2018中,對正規表示式進行瞭如下擴展:
正規表示式分組命名:
在ES2018之前,我們無法對正規表示式中的分組命名,在ES2018中引入了該特性,該特性既方便了正規的閱讀又方便了引用,
範例程式碼如下:
const RE_DATE = /(?<year>d{4})-(?<month>d{2})-(?<day>d {2})/ const matchObj = RE_DATE.exec('2022-02-22') const year = matchObj.groups.year // 2022 const month = matchObj.groups.month // 02 const day = matchObj.groups.day // 22
s修飾符/dotALl模式:新增的s修飾符允許使用.
匹配任意單個字符,****屬性表明是否在正則表達式中一起使用" s
"修飾符。
反向斷言: ES2018之前僅存在正向斷言,而ES2018中新增了反向斷言和反向否定斷言。
ES2015中新增數組的展開運算符,在ES2018中將此特性加入到了物件中,範例程式碼如下:
const n = { name: '一碗週' } const a = { age: 18 } const person = { ...n, ...a } // 合併物件console.log(person) // { name: '一碗週', age: 18 }
finally()
方法會傳回一個Promise
對象,當promise的狀態變更,不管是變成rejected
或fulfilled
,最終都會執行finally()
的回呼。
範例程式碼如下:
fetch(url) .then(res => { console.log(res) }) .catch(error => { console.log(error) }) .finally(() => { console.log('結束') })
在ES2019中最佳化了以下兩個內容:
Function.prototype.toString()
:傳回的函數體包含註解與空格;try...catch
:語句中的catch
允許不使用參數,範例程式碼如下:try { console.log('一碗週') } catch { console.error('一碗週') }
String.prototype.trimStart
:用於去除字串左邊的空格;String.prototype.trimLeft
:它是trimStart
的別名String.prototype.trimEnd
:用於trimEnd
String.prototype.trimRight
Array.prototype.flat()
;
const arr = [0, 1, 2, [3, 4]] console.log(arr.flat()) // [ 0, 1, 2, 3, 4 ]
Array.prototype.flatMap()
:此方法映射且扁平化數組,傳回新數組(只能展開一層數組)。ES2019新增的Object.fromEntries()
方法把鍵值對列表轉換為一個對象,是Object.entries()
方法的反操作,
範例程式碼如下:
const person = { name: '一碗週', age: '18', } const e = Object.entries(person) const p = Object.fromEntries(e) console.log(p) // { name: '一碗週', age: '18' }
description
是一個只讀屬性,它會傳回創建Symbol物件時的那個可選的描述字串。
模組化
在ES2020中加入了動態導入,也就是我們需要該模組的時候才會進行加載,這可以減少開銷和頁面加載時間,示例代碼如下:
import('/modules/my-module.js'). then(module => { // Do something with the module. })
動態導入使用import()
方法,它回傳一個Promise。
在ES2020中,也為import
增加一個meta
對象,該對象為JavaScript模組暴露了特定上下文的元資料屬性的對象。
BigInt的出現時解決JavaScript中允許的最大數字是2**53-1
的問題, BigInt
可以表示任意大的整數。
const theBiggestInt = 9007199254740991n; const alsoHuge = BigInt(9007199254740991); // ↪ 9007199254740991n const hugeString = BigInt("9007199254740991"); // ↪ 9007199254740991n const hugeHex = BigInt("0x1fffffffffffff"); // ↪ 9007199254740991n const hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111"); // ↪ 9007199254740991n
ES2020中引入globalThis
,它是對全域物件的引入,在Node是的全域物件是Global
,而瀏覽器環境是Window
;如下程式碼顯示的在有沒有GlobalThis
的差別:
// 之前var 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(); if (typeof globals.setTimeout !== 'function') { // no setTimeout in this environment! }
// 之後if (typeof globalThis.setTimeout !== 'function') { // no setTimeout in this environment! }
空值合併運算子是由兩個問號來表示,該運算子也是一個邏輯運算符,該運算子與邏輯或運算子類似。其計算規則為,只要左運算元為null
或undefined
,則傳回右邊運算元,否則傳回左運算元。而邏輯或運算子只有左運算元轉換為boolean
型別後為false
,就回傳右運算元。
範例程式碼如下:
console.log(null ?? 10) // 10 console.log(undefined ?? 10) // 10 console.log(false ?? 10) // false
此運算子用於為沒有值的變數賦值很有用,例如:如果這個數沒有值,就為其賦值,否則不賦值,
範例程式碼如下:
var value // 如果value的值不為null 或undefined 為其賦值10 value = value ?? 10 console.log(value) // 10
值得注意的是空值合併運算子與邏輯與和邏輯或不能同時使用,否則會拋出異常,解決方案是透過使用
()
來表明優先權
可選鏈運算子用於讀取某物件鏈下深處屬性的值,使用這個運算元不必驗證物件下的每個屬性必須存在,例如當我們想要存取Aab
這個屬性時,我們首先需要確保A
存在,然後要確保Aa
存在,才可以存取Aab
這個屬性,不然就會報錯。
使用可選鏈運算子就不會出現這樣的問題,當我們存取某個屬性時,只要有一處不存在,就會回傳undefind
,不會報錯。
var A = {} // console.log(Aab) // 報錯console.log(Aa?.b) // undefined
可選鏈運算子也可用於物件下方法的調用,範例程式碼如下:
var obj = {} // 如果存在obj.fun() 這個方法,下面則會直接調用,如果不存在則會返回undefined obj.fun?.A()
Promise.allSettled()
方法傳回一個在所有給定的promise 都已經resolved或rejected後的promise,並帶有一個物件數組,每個物件表示對應的promise結果。
replaceAll()
方法傳回一個新字串,新字串的內容是被取代的,實例程式碼如下:
const str = '一碗粥' const newStr = str.replaceAll('粥', '週') console.log(newStr) // 一碗週
嚴格意義上講數值分隔符號( _
)並不屬於一個運算符,其作用就是使數字更加利於閱讀,例如下面的代碼
console.log(1_0000_0000) // 100000000
這個符號僅僅起到了便於閱讀的目的,有與沒有的結果並不影響,看下面的代碼
1_1 === 11 // true
ES2021中新增的WeakRef
對象允許您保留對另一個對象的弱引用,而不會阻止被弱引用物件被GC回收。
ES2021新增的Promise.any()
方法,它接受的參數和與promise.all()
是一致的,唯一不同的是, Promise.any()
方法接受的可迭代物件中沒有一個promise成功(即所有的promises都失敗/拒絕),就傳回一個失敗的promise和AggregateError類型的實例。
ES2021中新增了一些賦值運算符,具體如下:
&&=
||=
??=
其實它與普通的賦值運算子一致,範例程式碼如下:
const [f1, f2, f3] = [ true, false] f1 &&= '一碗週' // 等同於str = str && '一碗週' f2 ||= '一碗週' // 等同於str = str || '一碗週' f3 ??= '一碗週' // 等同於str = str ?? '一碗週'
在ES2022中允許我們並不在constructor
中定義類別的成員,範例程式碼如下:
class C { myName = '一碗週' } /* 兩者是一致的*/ class C { constructor() { myName = '一碗週' } }
如果成員只宣告不初始化它的預設值是undefined。
在ES2022中允許我們使用#
開頭命名的變數作為類別的私有成員,
範例程式碼如下:
class C { #myName = '一碗週' } const c = new C() console.log(#myName) // Private field '#myName' must be declared in an enclosing class
在ES2022中新增了允許在頂層使用await
,在頂層可以不適用async
函數進行包裹,示例代碼如下:
import { AsyncFun } 從 'module' await AsyncFun() console.log(123)
Object.hasOwn()
方法用來判斷某個物件上是否具有某個屬性,範例程式碼如下:
const person = { name: '一碗週', age: 18, } console.log(Object.hasOwn(person, 'name')) // true console.log(Object.hasOwn(person, 'sex')) // false
ES2022中新增的at()
方法,它的作用是取得陣列中的某個成員,它的參數是陣列的索引,與直接使用索引的方式不同,它允許我們傳遞負值,等同於從後面倒數,範例程式碼如下:
const arr = [1, 2, 3, 4, 5, 6] console.log(arr.at(-1)) // 6 // 等同於arr[arr.length - 1]
正規表示式增加了一個/d
修飾符,當使用正規表示式的exec()
方法時,如果有/d
修飾符,那麼結果會多返回一個indices屬性,用來表示符合的結果的在原字串中的起始index值。
範例程式碼如下:
const str = 'JavaScript' const r = /a/d const m = r.exec(str) console.log(m.indices[0]) //[ 1, 2 ]
這篇文章到這就結束了,這篇文章中整理了ES2015到ES2022的新特性,有可能會有疏漏,望諒解。
腦圖如下: