JavaScript?ECMAScript?6(ES2015~ES2022)所有新特性總結
前言
自從ECMAScript在2015年正式發(fā)布了ES6起,到現在已經有了8個年頭了,而自從2015年起,每年的6月都會發(fā)布一個新的版本,以當時的年作為版本號。
在這眾多的版本中已經出現了很多新特性,為了方便記憶,我將所有的新特性全部整理在這一篇文章中。
ps:有的資料稱ES2015后的所有版本統(tǒng)稱為ES6,也有的稱ES6表示ES2015,ES7表示ES2016以此類推,這里不做討論。
目錄如下:

ES2015(ES6)
ES2015是改動最大的一個版本,基本上對ES2015之前的所有的內容都做了擴展,大體如下圖所示:

let、const關鍵字和塊級作用域
在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關鍵字聲明的變量不具有變量提升的特性,且存在暫時性死區(qū)的特性。
函數的擴展
在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是根據執(zhí)行上下文決定的,內部并不會綁定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() | 把字符的不同表示方法統(tǒng)一為同樣形式,返回新字符串(Unicode正規(guī)化) |
| 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():將類數組對象或者可迭代對象創(chuàng)建為一個新的數組,示例代碼如下:
function foo() {
return Array.from(arguments) // 將 arguments 轉換為數組
}
console.log(foo(1, 2, 3, 4, 5, 6)) // [ 1, 2, 3, 4, 5, 6 ]Array.of():創(chuàng)建一個具有可變數量參數的新數組實例,示例代碼如下:
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
}
// 靜態(tài)方法
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') // 觸發(fā) setter 訪問器
console.log(person.myName) // 觸發(fā) getter 訪問器模塊化
在ES2015中提出ESModel模塊化規(guī)范,這是第一個官方層面的模塊化規(guī)范,在這個規(guī)范中允許我們使用export導出模塊,使用import引入模塊,
示例代碼如下:
import a from 'm' // 導入模塊 m 中的默認導出,將其命名為 a
import a, { b } from 'm' // 導入模塊 m 中的默認導出以及單獨導入成員 b
import * as A from 'm' // 導入模塊中的所有成員
import 'm' // 執(zhí)行 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 }) // 一碗周18Symbol
Symbol是ES2015中新增的一種數據類型,通過Symbol()方法創(chuàng)建,可以傳遞一個字符串作為參數,用于描述該Symbol;
通過Symbol()方法創(chuàng)建的symbol值都是唯一的,示例代碼如下:
/**
* 語法
* Symbol([description])
* * description -> 是一個可選的描述信息
*/
// 創(chuàng)建一個 Symbol 類型的值
const mySymbol = Symbol()
console.log(mySymbol) // Symbol()
const myName = Symbol('一碗周')
console.log(typeof myName) // symbolSymbol還有一系列屬性和方法這里就不作介紹了。
Promise
Promise是ES2015中提供的一個異步解決方案,解決了回調地獄的問題。
通過Promise()構造函數可以創(chuàng)建一個promise對象,每一個Promise對象都具有以下幾種狀態(tài):
- pending: 初始狀態(tài),既不是成功,也不是失敗狀態(tài)。
- resolved: 意味著操作成功完成。
- rejected: 意味著操作失敗。
狀態(tài)的切換只有兩種,分別是:
- pending→resolved
- pending→resolved
一旦狀態(tài)發(fā)生改變,就不會再次改變
Promise實例中存在要給then方法,允許我們在Promise實例中鏈式調用,每個then方法還會返回一個Promise實例,
如下圖所示:

示例代碼如下:
new Promise((resolve, reject) => {
console.log('我是第一個Promise中的log')
resolve()
})
.then(() => {
console.log('我是第一個then中的log')
})
.then(() => {
console.log('我是第二個then中的log,但是我出現了異常')
throw new Error('Error')
})
.then(() => {
console.log('我是第三個then中的第一個回調的log,但是我不會執(zhí)行,因為我上面出現了異常')
}, () => {
console.log('我是第三個then中的第二個回調的log,我執(zhí)行了')
})
.then(() => {
console.log('我是第四個then中的log,我可以正常執(zhí)行')
})
/* 執(zhí)行結果如下
我是第一個Promise中的log
我是第一個then中的log
我是第二個then中的log,但是我出現了異常
我是第三個then中的第二個回調的log,我執(zhí)行了
我是第四個then中的log,我可以正常執(zhí)行
*/有關Promise的一些方法如下:
Promise.prototype.then():它最多需要有兩個參數:Promise的成功和失敗情況的回調函數;Promise.prototype.catch():等于then方法的第二個參數;Promise.all():將多個實例包裝成一個新實例,返回全部實例狀態(tài)變更后的結果數組(齊變更再返回)Promise.race():將多個實例包裝成一個新實例,返回全部實例狀態(tài)優(yōu)先變更后的結果(先變更先返回)Promise.resolve():將對象轉為Promise對象(等價于new Promise(resolve => resolve()))Promise.reject():將對象轉為狀態(tài)為rejected的Promise對象(等價于new Promise((resolve, reject) => reject()))
Iterator
Iterator即迭代器,它是一種接口,為各種不同的數據結構提供了統(tǒng)一的訪問機制,換句話說,只要有任何數據結構部署了迭代接口,就可以使用統(tǒng)一的方式的來遍歷它。
實現可迭代接口的數據結構,一般都自身實現或繼承了以Symbol.iterator屬性的,就屬于可迭代對象。Symbol.iterator屬性本身是一個函數,就是當前數據結構默認的遍歷器生成函數。
一個包含next()方法的對象,才可以稱為一個迭代對象。next()對象的會有返回一個對象,對象中包含兩個值,
如下所示:
value:迭代器返回的任何JavaScript值。done為true時可省略。done:一個布爾值,為false時表示迭代未停止,為true時立即停止迭代器,且可以省略value的值。
JavaScript原生提供的迭代器接口如下圖所示:

現在我們?yōu)閛bj來實現一個迭代器,代碼如下:
const obj = {
[Symbol.iterator] () {
return {
next () {
console.log('迭代器執(zhí)行了');
return {
value: '',
done: true // 標志是否結束,true表示已經結束
}
}
}
}
}我們在next()方法中添加了一個打印,為了驗證迭代器執(zhí)行了,最終的運行結果為
迭代器執(zhí)行了
Generator
Generator是ES2015中提供的一種異步編程解決方案,定義Generator函數在function關鍵字和函數名中間使用*星號,函數內部使用yield關鍵字定義不同的狀態(tài)。
示例代碼如下:
function* testGenerator() {
// yield定義一個狀態(tài)
yield '一碗周'
yield 'es新特性'
return 'generator' // 終結Generator,后面即使有yield關鍵字也無效
}
const g = testGenerator() // 返回 Generator 對象,通過next()方法移動狀態(tài)
g.next()
/* { value: '一碗周', done: false } */
g.next()
/* { value: 'es新特性', done: false } */
g.next()
/* { value: 'generator', done: true } */Proxy和Reffect
Proxy對象用于創(chuàng)建一個代理對象,從而實現基本操作的攔截和自定義,基本操作包含13種,如下表所示:
| 攔截 ?法 | 觸發(fā)?式 |
|---|---|
| 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(target) | Object.preventExtensions() |
| getOwnPropertyDescriptor(target, propKey) | Object.getOwnPropertyDescriptor() |
| defineProperty(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 rel="external nofollow" rel="stylesheet" />
<title>通過set自動更新dom</title>
</head>
<body>
<div class="card" style="width: 300px; margin: 100px auto">
<div class="card-body">
<h1 id="name"></h1>
<button id="btn" class="btn btn-primary">修改</button>
</div>
</div>
<script>
// 獲取DOM節(jié)點
const name = document.getElementById('name')
const btn = document.getElementById('btn')
// 定義一個修改值的函數
const updateDOM = (el, value) => {
el.innerHTML = value
}
const person = new Proxy({
name: '一碗粥',
}, {
set(target, propKey, value) {
// 如果里面的值改變就去調用我們的updateDOM
updateDOM(name, value)
target[propKey] = value
return true
},
})
name.innerHTML = person.name
// 點擊按鈕觸發(fā)修改操作
btn.addEventListener('click', () => {
person.name === '一碗周' ?
(person.name = '一碗粥') :
(person.name = '一碗周')
})
</script>
</body>
</html>上面的代碼就利用set方法進行數據綁定,如果對象發(fā)生改變,就自動更新我們的DOM。
Reflect是ECMAScript2015提供的一個對象,它提供了一些攔截JavaScript操作的靜態(tài)方法,這些方法與Proxy中的handlers中的方法一致。
Reflect并不是一個構造函數,也就是說它不能夠被實例化。
Proxy對象中的每一個攔截操作(例如:get、delete等),內部都對應的調用了Reflect的方法。它提供的靜態(tài)方法與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操作,實現創(chuàng)建類的實例 |
Set、Map、WeakSet、WeakMap
Set、Map、WeakSet、WeakMap是ES2015中新增的幾個對象:
Set和WeakSet與數組類似,準確的它他們是集合,這兩者的區(qū)別就是Set可以存儲任何數據類型,而WeakSet只能存儲對象的引用,而且是弱引用;
Set對象在實際開發(fā)中最常見的就是實現數據去重,示例代碼如下:
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與對象類似,存儲方式是鍵值對形式的,這兩者的區(qū)別Map的鍵值對都是可以是任意的而WeakMap鍵必須是對象的引用而值可以是任意類型的。
ES2016(ES7)
ES2016發(fā)布的新特性比較少,主要就兩個新特性,如下圖所示:

指數運算符
ES2016中新增指數**,也叫冪運算符,與Math.pow()有著一樣的功能,
示例代碼如下:
console.log(2 ** 10 === Math.pow(2, 10)) // true
Array.prototype.includes()方法
在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是相等的。
ES2017(ES8)

async/await語法糖
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改寫為平級的寫法。
Atomics對象
ES2017中新增了Atomics對象,該對象提供了一系列靜態(tài)方法用于操作SharedArrayBuffer和ArrayBuffer對象,該對象并不能使用new關鍵字進行實例化,僅僅提供了一些靜態(tài)的屬性和方法
對象擴展
在ES2017中為Object擴展了三個靜態(tài)方法,如下所示:
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(ES9)

異步迭代
在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 // 22s修飾符/dotALl模式:新增的s修飾符允許使用.匹配任意單個字符,****屬性表明是否在正則表達式中一起使用"s"修飾符。
反向斷言:ES2018之前僅存在正向斷言,而ES2018中新增了反向斷言和反向否定斷言。
對象展開運算符
ES2015中新增數組的展開運算符,在ES2018中將這一特性加入到了對象中,示例代碼如下:
const n = { name: '一碗周' }
const a = { age: 18 }
const person = { ...n, ...a }
// 合并對象
console.log(person) // { name: '一碗周', age: 18 }Promise.prototype.finally()
finally()方法會返回一個Promise對象,當promise的狀態(tài)變更,不管是變成rejected或者fulfilled,最終都會執(zhí)行finally()的回調。
示例代碼如下:
fetch(url)
.then(res => {
console.log(res)
})
.catch(error => {
console.log(error)
})
.finally(() => {
console.log('結束')
})ES2019(ES10)

優(yōu)化
在ES2019中優(yōu)化了以下兩個內容:
Function.prototype.toString():返回的函數體包含注釋與空格;try...catch:語句中的catch允許不使用參數,示例代碼如下:
try {
console.log('一碗周')
} catch {
console.error('一碗周')
}trimStart()/trimLeft()和trimEnd()/trimRight()
String.prototype.trimStart:用于去除字符串左邊的空格;String.prototype.trimLeft:它是trimStart的別名String.prototype.trimEnd:用于去除字符串右邊的空格;String.prototype.trimRight:它是trimEnd的別名
數組的擴展
在ES2019中擴展了兩個數組方法,分別是:
Array.prototype.flat():該方法會按照一個可指定的深度遞歸遍歷數組,并將所有元素與遍歷到的子數組中的元素合并為一個新數組返回;簡單的說就是實現數組的扁平化。
const arr = [0, 1, 2, [3, 4]] console.log(arr.flat()) // [ 0, 1, 2, 3, 4 ]
Array.prototype.flatMap():該方法映射且扁平化數組,返回新數組(只能展開一層數組)。
Object.fromEntries()
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' }Symbol.prototype.descrption
description是一個只讀屬性,它會返回創(chuàng)建Symbol對象時的那個可選的描述字符串。
ES2020(ES11)

模塊化
在ES2020中加入了動態(tài)導入,也就是我們需要該模塊的時候才會進行加載,這可以減少開銷和頁面加載時間,示例代碼如下:
import('/modules/my-module.js').then(module => {
// Do something with the module.
})動態(tài)導入使用import()方法,它返回一個Promise。
在ES2020中,還為import增加一個meta對象,該對象給JavaScript模塊暴露了特定上下文的元數據屬性的對象。
BigInt數據類型
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");
// ? 9007199254740991nglobalThis對象
ES2020中引入globalThis,它是對全局對象的引入,在Node是的全局對象是Global,而瀏覽器環(huán)境是Window;如下代碼展示的在有沒有GlobalThis的區(qū)別:
// 之前
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!
}空值合并運算符
空值合并運算符是由兩個問號來表示,該運算符也是一個邏輯運算符,該運算符與邏輯或運算符類似。其計算規(guī)則為,只要左運算元為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
值得注意的是空值合并運算符與邏輯與和邏輯或不能同時使用,否則會拋出異常,解決方案是通過使用
()來表明優(yōu)先級
可選鏈操作符
可選鏈操作符用于讀取某對象鏈下深處屬性的值,使用這個操作符不必驗證對象下的每個屬性必須存在,例如我們想要訪問A.a.b這個屬性時,我們首先需要確保A存在,然后需要確保A.a存在,才可以訪問A.a.b這個屬性,不然就會報錯。
使用可選鏈操作符就不會出現這樣的問題,當我們訪問某個屬性時,只要有一處不存在,就會返回undefind,不會報錯。
var A = {}
// console.log(A.a.b) // 報錯
console.log(A.a?.b) // undefined可選鏈操作符也可用于對象下方法的調用,示例代碼如下:
var obj = {}
// 如果存在 obj.fun() 這個方法,下面則會直接調用,如果不存在則會返回undefined
obj.fun?.A()Promise.allSettled()
Promise.allSettled()方法返回一個在所有給定的 promise 都已經resolved或rejected后的 promise,并帶有一個對象數組,每個對象表示對應的 promise 結果。
ES2021(ES12)

String.prototype.replaceAll()
replaceAll()方法返回一個新字符串,新字符串的內容是經過替換的,實例代碼如下:
const str = '一碗粥'
const newStr = str.replaceAll('粥', '周')
console.log(newStr) // 一碗周數值分隔符
嚴格意義上講數值分隔符(_)并不屬于一個運算符,其作用就是使數字更加利于閱讀,例如下面的代碼
console.log(1_0000_0000) // 100000000
這個符號僅僅起到了便于閱讀的目的,有與沒有的結果并不影響,看下面的代碼
1_1 === 11 // true
WeakRef
ES2021中新增的WeakRef對象允許您保留對另一個對象的弱引用,而不會阻止被弱引用對象被GC回收。
Promise.any()
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

class的擴展
在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 classawait在頂層使用
在ES2022中新增了允許在頂層使用await,在頂層可以不適用async函數進行包裹,示例代碼如下:
import { AsyncFun } from 'module'
await AsyncFun()
console.log(123)Object.hasOwn()
Object.hasOwn()方法用于判斷某個對象上是否具有某個屬性,示例代碼如下:
const person = {
name: '一碗周',
age: 18,
}
console.log(Object.hasOwn(person, 'name')) // true
console.log(Object.hasOwn(person, 'sex')) // falseArray.prototype.at()
ES2022中新增的at()方法,它的作用是獲取數組中的某個成員,它的參數是數組的索引,與直接使用索引的方式不同,它允許我們傳遞負值,等同于從后面倒數,示例代碼如下:
const arr = [1, 2, 3, 4, 5, 6] console.log(arr.at(-1)) // 6 // 等同于 arr[arr.length - 1]
d修飾符
正則表達式增加了一個/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的新特性,有可能會有疏漏,望諒解。
腦圖如下:

到此這篇關于JavaScript ECMAScript 6(ES2015~ES2022)所有新特性總結的文章就介紹到這了,更多相關JS ECMAScript內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

