文章目录
- 前言
- 一、ES2016
-
- [1.1 Array.prototype.includes](#1.1 Array.prototype.includes)
- [1.2 指数运算符](#1.2 指数运算符)
- 二、ES2017
-
- [2.1 async / await](#2.1 async / await)
- [2.2 Object.entries / Object.values](#2.2 Object.entries / Object.values)
- [2.3 String.prototype.padStart / padEnd](#2.3 String.prototype.padStart / padEnd)
- [2.4 函数参数尾逗号](#2.4 函数参数尾逗号)
- 三、ES2018
-
- [3.1 Rest / Spread 属性](#3.1 Rest / Spread 属性)
- [3.2 Promise.prototype.finally](#3.2 Promise.prototype.finally)
- [3.3 异步迭代](#3.3 异步迭代)
- 四、ES2019
-
- [4.1 Array.prototype.flat / flatMap](#4.1 Array.prototype.flat / flatMap)
- [4.2 Object.fromEntries](#4.2 Object.fromEntries)
- [4.3 可选的 catch 绑定](#4.3 可选的 catch 绑定)
- 五、ES2020
-
- [5.1 可选链操作符 ?.](#5.1 可选链操作符 ?.)
- [5.2 空值合并操作符 ??](#5.2 空值合并操作符 ??)
- [5.3 BigInt](#5.3 BigInt)
- [5.4 globalThis](#5.4 globalThis)
- [5.5 Promise.allSettled](#5.5 Promise.allSettled)
- [5.6 顶层 await](#5.6 顶层 await)
- 六、ES2021
-
- [6.1 String.prototype.replaceAll](#6.1 String.prototype.replaceAll)
- [6.2 Promise.any](#6.2 Promise.any)
- [6.3 逻辑赋值运算符](#6.3 逻辑赋值运算符)
- [6.4 数值分隔符](#6.4 数值分隔符)
- 七、ES2022
-
- [7.1 Object.hasOwn](#7.1 Object.hasOwn)
- [7.2 Array.prototype.at](#7.2 Array.prototype.at)
- [7.3 class 私有属性和方法](#7.3 class 私有属性和方法)
- [7.4 Error.cause](#7.4 Error.cause)
- 八、ES2023
-
- [8.1 Array 的非破坏性方法](#8.1 Array 的非破坏性方法)
- 九、ES2024
-
- [9.1 Object.groupBy / Map.groupBy](#9.1 Object.groupBy / Map.groupBy)
- [9.2 Promise.withResolvers](#9.2 Promise.withResolvers)
- 十、面试聚焦
-
- [10.1 ?? vs || 的区别](#10.1 ?? vs || 的区别)
- [10.2 顶层 await 的条件](#10.2 顶层 await 的条件)
- 十一、易混淆点
- 十二、思考与练习
- 总结
前言
ES2016(ES7)到 ES2024(ES15)引入了大量实用特性,掌握这些新特性能让代码更简洁、更安全。本篇速览各版本的重要更新。
一、ES2016
1.1 Array.prototype.includes
javascript
const arr = [1, 2, 3, NaN]
// indexOf 无法正确判断 NaN
arr.indexOf(NaN) // -1
// includes 可以正确判断 NaN
arr.includes(NaN) // true
arr.includes(2) // true
arr.includes(4) // false
1.2 指数运算符
javascript
// 之前
Math.pow(2, 10) // 1024
// ES2016
2 ** 10 // 1024
// 结合赋值
let x = 2
x **= 3 // x = x ** 3 = 8
二、ES2017
2.1 async / await
javascript
// 之前:Promise 链
function fetchData() {
return fetch('/api/user')
.then(res => res.json())
.then(user => fetch(`/api/posts/${user.id}`))
.then(res => res.json())
}
// ES2017:async/await
async function fetchData() {
const res = await fetch('/api/user')
const user = await res.json()
const postsRes = await fetch(`/api/posts/${user.id}`)
return postsRes.json()
}
// 错误处理
async function safeFetch(url) {
try {
const res = await fetch(url)
return await res.json()
} catch (err) {
console.error('请求失败:', err.message)
return null
}
}
2.2 Object.entries / Object.values
javascript
const obj = { a: 1, b: 2, c: 3 }
// Object.entries:返回键值对数组
Object.entries(obj) // [['a', 1], ['b', 2], ['c', 3]]
// Object.values:返回值数组
Object.values(obj) // [1, 2, 3]
// 配合 for...of 遍历
for (const [key, value] of Object.entries(obj)) {
console.log(key, value)
}
2.3 String.prototype.padStart / padEnd
javascript
// 补齐字符串长度
'5'.padStart(2, '0') // '05'
'hello'.padEnd(10, '!') // 'hello!!!!!'
// 常用于格式化
const time = '9'
`${time.padStart(2, '0')}:00` // '09:00'
2.4 函数参数尾逗号
javascript
// 允许函数参数列表末尾有逗号
function add(
a,
b, // 尾逗号,方便添加新参数
) {
return a + b
}
三、ES2018
3.1 Rest / Spread 属性
javascript
// Rest 属性
const { a, ...rest } = { a: 1, b: 2, c: 3 }
// a = 1, rest = { b: 2, c: 3 }
// Spread 属性
const merged = { a: 1, ...{ b: 2, c: 3 } }
// { a: 1, b: 2, c: 3 }
3.2 Promise.prototype.finally
javascript
fetch('/api/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => {
// 无论成功或失败都会执行
hideLoading()
})
3.3 异步迭代
javascript
// for await...of
async function* asyncGenerator() {
yield await Promise.resolve(1)
yield await Promise.resolve(2)
yield await Promise.resolve(3)
}
for await (const value of asyncGenerator()) {
console.log(value) // 1, 2, 3
}
四、ES2019
4.1 Array.prototype.flat / flatMap
javascript
const arr = [1, [2, 3], [4, [5, 6]]]
// flat:扁平化数组
arr.flat() // [1, 2, 3, 4, [5, 6]]
arr.flat(2) // [1, 2, 3, 4, 5, 6]
arr.flat(Infinity) // 完全扁平化
// flatMap:先 map 再 flat
const arr2 = [1, 2, 3]
arr2.flatMap(x => [x, x * 2]) // [1, 2, 2, 4, 3, 6]
4.2 Object.fromEntries
javascript
// Object.entries 的逆操作
const entries = [['a', 1], ['b', 2]]
Object.fromEntries(entries) // { a: 1, b: 2 }
// 常用于转换 URL 参数
const url = new URLSearchParams('foo=1&bar=2')
Object.fromEntries(url) // { foo: '1', bar: '2' }
4.3 可选的 catch 绑定
javascript
// 之前:必须有 catch 参数
try {
JSON.parse('invalid')
} catch (e) {
// 必须写 e,即使不使用
}
// ES2019:可以省略 catch 参数
try {
JSON.parse('invalid')
} catch {
// 不需要写参数
}
五、ES2020
5.1 可选链操作符 ?.
javascript
const user = {
address: {
street: 'Main St'
}
}
// 之前:需要层层判断
const street = user && user.address && user.address.street
// ES2020:可选链
const street = user?.address?.street
// 调用方法
user?.getName?.()
// 访问数组元素
arr?.[0]
5.2 空值合并操作符 ??
javascript
const value = null ?? 'default' // 'default'
const value2 = 0 ?? 'default' // 0
const value3 = '' ?? 'default' // ''
// 与 || 的区别
const value4 = 0 || 'default' // 'default'(0 是 falsy)
const value5 = 0 ?? 'default' // 0(只判断 null/undefined)
// ?? 只判断 null 和 undefined
// || 判断所有 falsy 值(0, '', false, null, undefined, NaN)
5.3 BigInt
javascript
// 超大整数
const max = Number.MAX_SAFE_INTEGER // 9007199254740991
const big = 9007199254740991n // BigInt
const big2 = BigInt(9007199254740991)
// 运算
const sum = big + 1n // 9007199254740992n
// 注意:BigInt 和 Number 不能混合运算
// const result = big + 1 // TypeError
5.4 globalThis
javascript
// 获取全局对象的统一方式
// 浏览器是 window,Node.js 是 global,Web Worker 是 self
globalThis // 统一的全局对象
5.5 Promise.allSettled
javascript
const promises = [
Promise.resolve(1),
Promise.reject('error'),
Promise.resolve(3)
]
// Promise.all:任一 reject 就立即 reject
Promise.all(promises) // reject 'error'
// Promise.allSettled:等待所有完成
Promise.allSettled(promises)
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: 'error' },
// { status: 'fulfilled', value: 3 }
// ]
5.6 顶层 await
javascript
// 在模块顶层可以使用 await
const response = await fetch('/api/data')
const data = await response.json()
// 限制:只能在 ES 模块中使用(type="module" 或 .mjs)
六、ES2021
6.1 String.prototype.replaceAll
javascript
const str = 'aabbcc'
// 之前:正则
str.replace(/a/g, 'x') // 'xxbbcc'
// ES2021
str.replaceAll('a', 'x') // 'xxbbcc'
6.2 Promise.any
javascript
const promises = [
Promise.reject('error1'),
Promise.reject('error2'),
Promise.resolve(3)
]
// 任一 resolve 就 resolve
Promise.any(promises) // resolve 3
// 全部 reject 才 reject
// AggregateError: All promises were rejected
6.3 逻辑赋值运算符
javascript
// &&=
a &&= b // 等价于 a && (a = b)
// ||=
a ||= b // 等价于 a || (a = b)
// ??=
a ??= b // 等价于 a ?? (a = b)
// 示例
let config = {}
config.timeout ??= 3000 // 如果 timeout 是 null/undefined,则设置为 3000
6.4 数值分隔符
javascript
// 提高大数字的可读性
const billion = 1_000_000_000
const bytes = 0xFF_FF_FF_FF
const fraction = 0.123_456_789
// 分隔符不影响数值
1_000 === 1000 // true
七、ES2022
7.1 Object.hasOwn
javascript
const obj = { a: 1 }
// 之前:hasOwnProperty
obj.hasOwnProperty('a') // true
// ES2022:Object.hasOwn(推荐)
Object.hasOwn(obj, 'a') // true
// 优势:即使对象没有继承 Object.prototype 也能用
const obj2 = Object.create(null)
obj2.a = 1
obj2.hasOwnProperty('a') // TypeError
Object.hasOwn(obj2, 'a') // true
7.2 Array.prototype.at
javascript
const arr = [1, 2, 3, 4, 5]
// 之前:获取最后一个元素
arr[arr.length - 1] // 5
arr.slice(-1)[0] // 5
// ES2022:at()
arr.at(-1) // 5
arr.at(0) // 1
arr.at(-2) // 4
7.3 class 私有属性和方法
javascript
class Counter {
// 私有属性
#count = 0
// 静态私有属性
static #total = 0
// 私有方法
#increment() {
this.#count++
}
increment() {
this.#increment()
Counter.#total++
}
get count() {
return this.#count
}
}
7.4 Error.cause
javascript
try {
try {
JSON.parse('invalid')
} catch (err) {
throw new Error('Parse failed', { cause: err })
}
} catch (err) {
console.log(err.message) // 'Parse failed'
console.log(err.cause.message) // 'Unexpected token...'
}
八、ES2023
8.1 Array 的非破坏性方法
javascript
const arr = [3, 1, 2]
// toSorted:非破坏性排序
arr.toSorted() // [1, 2, 3],原数组不变
arr // [3, 1, 2]
// toReversed:非破坏性反转
arr.toReversed() // [2, 1, 3],原数组不变
// toSpliced:非破坏性 splice
arr.toSpliced(1, 1) // [3, 2],原数组不变
// with:非破坏性修改指定位置
arr.with(0, 10) // [10, 1, 2],原数组不变
九、ES2024
9.1 Object.groupBy / Map.groupBy
javascript
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 25 }
]
// 按年龄分组
const grouped = Object.groupBy(people, p => p.age)
// {
// 25: [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }],
// 30: [{ name: 'Bob', age: 30 }]
// }
9.2 Promise.withResolvers
javascript
// 之前:需要在外部定义 resolve/reject
let resolve, reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
// ES2024
const { promise, resolve, reject } = Promise.withResolvers()
十、面试聚焦
10.1 ?? vs || 的区别
javascript
// || 判断所有 falsy 值
0 || 'default' // 'default'
'' || 'default' // 'default'
false || 'default' // 'default'
null || 'default' // 'default'
// ?? 只判断 null 和 undefined
0 ?? 'default' // 0
'' ?? 'default' // ''
false ?? 'default' // false
null ?? 'default' // 'default'
10.2 顶层 await 的条件
javascript
// 只能在 ES 模块中使用顶层 await
// 1. <script type="module">
// 2. .mjs 文件
// 3. package.json 中 "type": "module"
// CommonJS 模块中不能使用
// 顶层 await 会阻塞当前模块的加载
十一、易混淆点
- ?? vs || :
??只判断null/undefined,||判断所有 falsy 值(0, '', false 等)。 - ?. 的短路特性 :
a?.b.c中如果a是 null/undefined,b.c不会执行。 - 顶层 await:只能在 ES 模块中使用,会阻塞当前模块的加载。
- Object.hasOwn vs hasOwnProperty :
Object.hasOwn更安全,即使对象没有继承Object.prototype也能用。 - ES2023 非破坏性方法 :
toSorted、toReversed等返回新数组,不修改原数组。
十二、思考与练习
1. 0 ?? 'default' 和 0 || 'default' 的结果分别是什么?为什么?
解析:
0 ?? 'default'→0(?? 只判断 null/undefined)0 || 'default'→'default'(|| 判断所有 falsy 值,0 是 falsy)
2. 什么是顶层 await?有什么限制?
解析:
- 顶层 await 允许在模块顶层使用 await,无需包裹在 async 函数中
- 限制:只能在 ES 模块中使用(
<script type="module">或.mjs)
3. Object.hasOwn 比 hasOwnProperty 好在哪里?
解析:
hasOwnProperty依赖原型链,如果对象没有继承Object.prototype会报错Object.hasOwn是静态方法,不依赖原型链,更安全
4. Array.prototype.at(-1) 的作用是什么?
解析:获取数组的最后一个元素,等价于 arr[arr.length - 1],但更简洁,支持负索引。
5. ES2023 的 toSorted 和 sort 有什么区别?
解析:
sort()是破坏性的,会修改原数组toSorted()是非破坏性的,返回新数组,原数组不变
总结
- ES2016 :
includes、**指数运算符 - ES2017 :
async/await、Object.entries/values、padStart/padEnd - ES2018 :Rest/Spread 属性、
Promise.finally、异步迭代 - ES2019 :
flat/flatMap、Object.fromEntries、可选 catch 绑定 - ES2020 :
?.可选链、??空值合并、BigInt、globalThis、Promise.allSettled、顶层 await - ES2021 :
replaceAll、Promise.any、逻辑赋值运算符、数值分隔符 - ES2022 :
Object.hasOwn、at()、class 私有属性/方法、Error.cause - ES2023 :
toSorted/toReversed/toSpliced/with非破坏性方法 - ES2024 :
Object.groupBy、Promise.withResolvers