JavaScript ES6+ 全部特性
ES6(ES2015)至 ES2024 的全部重要特性,覆盖日常开发中最高频的语法。
变量声明
let / const
js
// const —— 常量,不可重新赋值
const API_URL = 'https://api.example.com'
const MAX_SIZE = 100
// let —— 块级作用域变量
let count = 0
count++
// ❌ const 不能重新赋值
// API_URL = 'xxx' // TypeError
// ⚠️ const 对象/数组的内部可以修改
const user = { name: '张三' }
user.name = '李四' // ✅ 修改属性
// user = {} // ❌ 重新赋值
const list = [1, 2, 3]
list.push(4) // ✅ 修改内容
// list = [] // ❌ 重新赋值解构赋值
js
// ---- 数组解构 ----
const [first, second, third] = [1, 2, 3]
const [head, ...tail] = [1, 2, 3, 4]
// head = 1, tail = [2, 3, 4]
// 跳过元素
const [, second] = [1, 2, 3] // second = 2
// 默认值
const [posX = 0, posY = 0] = [1] // posX = 1, posY = 0
// ---- 对象解构 ----
const { name, age } = { name: '张三', age: 25 }
// 重命名
const { name: userName, age: userAge } = { name: '张三', age: 25 }
// 默认值
const { role = 'user', status = 'active' } = {}
// 嵌套解构
const { address: { city } } = { address: { city: '北京' } } // city = '北京'
// 剩余属性
const { id, ...userInfo } = { id: 1, name: '张三', age: 25 }
// userInfo = { name: '张三', age: 25 }解构的常见用法
js
// 函数参数解构
function createUser({ name, age, role = 'user' }) {
return { name, age, role }
}
// 交换变量
let swapA = 1, swapB = 2
;[swapA, swapB] = [swapB, swapA] // swapA = 2, swapB = 1
// 从 API 响应提取数据
const { data: { user, token } } = await api.login(credentials)
// 遍历 Map
const map = new Map([['a', 1], ['b', 2]])
for (const [key, value] of map) {
console.log(key, value)
}箭头函数
js
// 基本语法
const add = (a, b) => a + b
const square = (n) => n * n
const greet = () => 'hello'
// 函数体有多条语句
const calculate = (a, b) => {
const sum = a + b
return sum * 2
}
// 返回对象(需要括号)
const createUser = (name, age) => ({ name, age })
// 高阶函数常用
const numbers = [1, 2, 3, 4, 5]
const doubled = numbers.map((n) => n * 2)
const evens = numbers.filter((n) => n % 2 === 0)
const sum = numbers.reduce((acc, n) => acc + n, 0)箭头函数 vs 普通函数
| 特性 | 箭头函数 | 普通函数 |
|---|---|---|
| this | 继承外层(词法 this) | 调用时绑定 |
| arguments | 没有 | 有 |
| prototype | 没有 | 有 |
| 构造函数 | 不能 new | 可以 new |
| yield | 不能用 | 可以用 |
js
// ❌ 不适合用箭头函数的场景
const obj = {
name: '张三',
// 方法:this 会丢失
greet: () => {
console.log(this.name) // undefined
},
// ✅ 用简写方法
greet() {
console.log(this.name) // '张三'
},
}模板字符串
js
const name = '张三'
const age = 25
// 基本用法
const msg = `我叫 ${name},今年 ${age} 岁`
// 多行
const html = `
<div class="card">
<h2>${name}</h2>
<p>年龄:${age}</p>
</div>
`
// 表达式
const price = 100
const text = `总价:${price * 0.8} 元(八折)`
// 标签模板(高级用法)
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] ? `<mark>${values[i]}</mark>` : '')
}, '')
}
const highlighted = highlight`用户 ${name} 的年龄是 ${age}`
// "用户 <mark>张三</mark> 的年龄是 <mark>25</mark>"展开运算符与剩余参数
js
// ---- 展开运算符 ----
// 数组展开(ES2015)
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const merged = [...arr1, ...arr2] // [1, 2, 3, 4, 5, 6]
// 对象展开(ES2018)
const defaults = { theme: 'light', lang: 'zh-CN' }
const userConfig = { theme: 'dark' }
const config = { ...defaults, ...userConfig } // { theme: 'dark', lang: 'zh-CN' }
// 浅拷贝
const copy = { ...original }
const arrCopy = [...original]
// ---- 剩余参数 ----
function sum(...numbers) {
return numbers.reduce((acc, n) => acc + n, 0)
}
sum(1, 2, 3) // 6
function log(first, ...rest) {
console.log('第一个:', first)
console.log('其余:', rest)
}
log('a', 'b', 'c', 'd')
// 第一个: a
// 其余: ['b', 'c', 'd']对象增强
js
const name = '张三'
const age = 25
// 属性简写
const user = { name, age } // 等价于 { name: name, age: age }
// 方法简写
const obj = {
// 等价于 greet: function () { ... }
greet() {
return 'hello'
},
}
// 计算属性名
const key = 'color'
const style = {
[key]: 'red',
[`${key}Size`]: '14px',
}
// { color: 'red', colorSize: '14px' }
// 可选链(?.)
const city = user?.address?.city // undefined(不会报错)
// 空值合并(??)
const port = config.port ?? 3000 // 只有 null/undefined 才用默认值
const port2 = config.port || 3000 // 0, '', false 也会用默认值数组常用方法
数组的完整方法(增删、查找、遍历、排序、转换等)详见 数据类型与内置方法 - Array 原型方法。
以下为 ES6+ 新增的数组方法:
flat / flatMap(ES2019)
js
const nested = [1, [2, 3], [4, [5, 6]]]
nested.flat() // [1, 2, 3, 4, [5, 6]]
nested.flat(2) // [1, 2, 3, 4, 5, 6]
nested.flat(Infinity) // 完全扁平化
const sentences = ['hello world', 'foo bar']
const words = sentences.flatMap((s) => s.split(' '))
// ['hello', 'world', 'foo', 'bar']at(ES2022)
js
const arr = [1, 2, 3, 4, 5]
arr.at(0) // 1
arr.at(-1) // 5(最后一个)
arr.at(-2) // 4(倒数第二个)findLast / findLastIndex(ES2023)
js
const arr = [1, 2, 3, 4, 5]
arr.find((x) => x > 3) // 4(从前往后)
arr.findLast((x) => x > 3) // 5(从后往前)
arr.findIndex((x) => x > 3) // 3
arr.findLastIndex((x) => x > 3) // 4Promise
大白话解释: Promise 就像"点外卖等通知"。你下单(发起异步操作),外卖小哥去取餐(执行中),然后你会收到通知:
- 成功(resolve):餐到了,可以吃了
- 失败(reject):餐厅关门了,订单取消
为什么要用 Promise?
- 解决回调地狱:不用层层嵌套回调函数,代码更易读
- 链式调用:
.then().then().then()像流水线一样 - 错误处理:一个
.catch()捕获所有错误 - 并行执行:
Promise.all()同时执行多个异步操作
Promise 的三种状态:
- Pending:等待中(外卖还没到)
- Fulfilled:成功(餐到了)
- Rejected:失败(订单取消)
基本用法
js
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
// reject(new Error('失败'))
}, 1000)
})
promise
.then((result) => console.log(result))
.catch((error) => console.error(error))
.finally(() => console.log('结束'))Promise 静态方法
js
// Promise.all —— 全部成功才成功
const results = await Promise.all([
fetch('/api/user'),
fetch('/api/orders'),
fetch('/api/settings'),
])
// Promise.allSettled —— 等待全部完成(不论成败)
const outcomes = await Promise.allSettled([
fetch('/api/a'),
fetch('/api/b'),
])
// [{ status: 'fulfilled', value: ... }, { status: 'rejected', reason: ... }]
// Promise.race —— 最快的结果(不论成功失败)
const fastest = await Promise.race([
fetch('/api/fast'),
new Promise((_, reject) => setTimeout(() => reject('超时'), 5000)),
])
// Promise.any —— 最快的成功结果
const firstSuccess = await Promise.any([
fetch('/api/a'),
fetch('/api/b'),
fetch('/api/c'),
])常见模式
js
// 串行执行
async function sequential() {
const user = await fetchUser()
const orders = await fetchOrders(user.id)
const details = await fetchOrderDetails(orders[0].id)
return details
}
// 并行执行
async function parallel() {
const [user, settings] = await Promise.all([
fetchUser(),
fetchSettings(),
])
return { user, settings }
}
// 限制并发数
async function limitConcurrency(tasks, limit) {
const results = []
const executing = new Set()
for (const task of tasks) {
const p = task().then((result) => {
executing.delete(p)
return result
})
executing.add(p)
results.push(p)
if (executing.size >= limit) {
await Promise.race(executing)
}
}
return Promise.all(results)
}async / await
大白话解释: async/await 是 Promise 的"语法糖"。让异步代码看起来像同步代码,更易读。
- async:声明一个函数是异步的
- await:暂停等待 Promise 完成,拿到结果后继续执行
为什么要用 async/await?
- 代码更易读:不用写
.then(),像写同步代码一样 - 错误处理更简单:用
try/catch捕获错误 - 调试更方便:可以像调试同步代码一样打断点
async/await vs Promise.then:
- Promise.then:链式调用,适合简单的异步操作
- async/await:更易读,适合复杂的异步流程
js
// 基本用法
async function fetchUser() {
try {
const res = await fetch('/api/user')
if (!res.ok) throw new Error(`HTTP ${res.status}`)
const data = await res.json()
return data
} catch (error) {
console.error('获取用户失败:', error)
throw error
}
}
// 箭头函数
const getUser = async (id) => {
const res = await fetch(`/api/user/${id}`)
return res.json()
}
// for 循环中的 await(串行)
async function processItems(items) {
for (const item of items) {
await processItem(item) // 一个一个执行
}
}
// for 循环中的 await(并行)
async function processItemsParallel(items) {
await Promise.all(items.map((item) => processItem(item))) // 同时执行
}错误处理
js
// 方案一:try/catch
async function fetchData() {
try {
const data = await riskyOperation()
return data
} catch (error) {
return defaultValue
}
}
// 方案二:toTuple 模式
async function toTuple(promise) {
try {
const result = await promise
return [null, result]
} catch (error) {
return [error, null]
}
}
const [err, data] = await toTuple(fetchUser())
if (err) {
console.error('失败:', err)
} else {
console.log('成功:', data)
}Map 与 Set
Map
js
const map = new Map()
map.set('name', '张三')
map.set(1, 'one')
map.set(true, 'yes')
map.get('name') // '张三'
map.has(1) // true
map.size // 3
map.delete(1)
// 遍历
for (const [key, value] of map) {
console.log(key, value)
}
// 与对象的区别
// - Map 的 key 可以是任意类型
// - Map 有 size 属性
// - Map 可直接遍历
// - Map 在频繁增删场景性能更好Set
js
const set = new Set([1, 2, 3, 2, 1])
// Set { 1, 2, 3 }
set.add(4)
set.has(2) // true
set.delete(1)
set.size // 3
// 数组去重
const unique = [...new Set([1, 2, 2, 3, 3, 3])] // [1, 2, 3]
// 交集、并集、差集
const setA = new Set([1, 2, 3])
const setB = new Set([2, 3, 4])
const union = new Set([...setA, ...setB]) // {1, 2, 3, 4}
const intersection = new Set([...setA].filter((x) => setB.has(x))) // {2, 3}
const difference = new Set([...setA].filter((x) => !setB.has(x))) // {1}模块化
导出
js
// utils/math.js
// 命名导出
export const PI = 3.14159
export function add(a, b) { return a + b }
export function multiply(a, b) { return a * b }
// 默认导出(每个模块只能一个)
export default class Calculator {
// ...
}导入
js
// 命名导入
import { add, multiply, PI } from './utils/math'
// 默认导入
import Calculator from './utils/math'
// 全部导入
import * as math from './utils/math'
math.add(1, 2)
// 重命名导入
import { add as sum } from './utils/math'
// 动态导入(按需加载)
const module = await import('./utils/math')
module.add(1, 2)ES2016
js
// Array.prototype.includes
const arr = [1, 2, 3, NaN]
arr.includes(2) // true
arr.includes(4) // false
arr.includes(NaN) // true(indexOf 找不到 NaN)
// 指数运算符
2 ** 10 // 1024
2 ** 3 // 8
let base = 2
base **= 3 // base = 8(等价于 base = base ** 3)ES2017
js
// Object.entries / Object.values(ES2017 正式标准)
const obj = { a: 1, b: 2, c: 3 }
Object.entries(obj) // [['a',1], ['b',2], ['c',3]]
Object.values(obj) // [1, 2, 3]
// Object.getOwnPropertyDescriptors(ES2017,获取所有属性的描述符)
// 常用于浅拷贝对象(包括 getter/setter)
const source = {
_name: '张三',
get name() { return this._name },
set name(value) { this._name = value },
}
const descs = Object.getOwnPropertyDescriptors(source)
// {
// _name: { value: '张三', writable: true, enumerable: true, configurable: true },
// name: { get: [Function], set: [Function], enumerable: true, configurable: true }
// }
// ❌ 展开运算符会丢失 getter/setter
const copy1 = { ...source } // copy1.name 是值,不是 getter/setter
// ✅ 用 getOwnPropertyDescriptors + defineProperties 完整拷贝
const copy2 = Object.defineProperties({}, descs)
copy2.name // '张三'(仍是 getter)
// 函数参数尾逗号
function foo(
a,
b,
c, // ✅ 允许尾逗号
) {}
const arr = [
1,
2,
3, // ✅
]
// String.prototype.padStart / padEnd(字符串填充)
'5'.padStart(3, '0') // '005'
'hello'.padEnd(10, '!') // 'hello!!!!!'
'hi'.padStart(5) // ' hi'(默认用空格填充)
// SharedArrayBuffer / Atomics(多线程,略)ES2018
js
// 对象展开 / 剩余属性(已在 ES6 章节介绍)
const { a, ...rest } = { a: 1, b: 2, c: 3 }
const merged = { ...obj1, ...obj2 }
// Promise.prototype.finally
fetch('/api/data')
.then((res) => res.json())
.catch((err) => console.error(err))
.finally(() => {
loading.value = false // 无论成功失败都执行
})
// 异步迭代(for-await-of)
async function processPages(urls) {
for await (const response of urls.map((url) => fetch(url))) {
const data = await response.json()
console.log(data)
}
}
// 正则:命名捕获组
const match = '2024-01-15'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/)
console.log(match.groups.year) // '2024'
console.log(match.groups.month) // '01'
// 正则:反向断言(lookbehind)
'hello world'.match(/(?<=hello\s)world/) // ['world']
'hello world'.match(/(?<!hello\s)world/) // null
// 正则:s 标志(dotAll,. 匹配换行符)
/./s.test('\n') // trueES2019
js
// Array.prototype.flat / flatMap
;[1, [2, [3]]].flat() // [1, 2, [3]]
;[1, [2, [3]]].flat(Infinity) // [1, 2, 3]
;[1, 2, 3].flatMap((x) => [x, x * 2]) // [1, 2, 2, 4, 3, 6]
// Object.fromEntries(entries 的逆操作)
Object.fromEntries([['a', 1], ['b', 2]]) // { a: 1, b: 2 }
// 将 URLSearchParams 转对象
const params = new URLSearchParams('a=1&b=2')
Object.fromEntries(params) // { a: '1', b: '2' }
// String.prototype.trimStart / trimEnd
' hello '.trimStart() // 'hello '
' hello '.trimEnd() // ' hello'
// 可选的 catch 绑定
try {
// ...
} catch { // 不需要 (error) 参数
console.log('出错了')
}
// Symbol.prototype.description
Symbol('foo').description // 'foo'ES2020
js
// 可选链(?.)—— 已在「对象增强」章节介绍
// 空值合并(??)—— 已在「对象增强」章节介绍
// BigInt
const big = 9007199254740991n
const big2 = BigInt('9007199254740991')
big + 1n // 9007199254740992n
big === BigInt(9007199254740991) // true
typeof big // 'bigint'
// String.prototype.matchAll(返回所有匹配的迭代器)
const str = 'test1test2test3'
const regex = /test(\d)/g
const matches = [...str.matchAll(regex)]
// [['test1','1'], ['test2','2'], ['test3','3']]
// Promise.allSettled —— 已在 Promise 章节介绍
// globalThis(统一的全局对象引用)
// 浏览器:globalThis === window
// Node.js:globalThis === global
// Worker:globalThis === self
console.log(globalThis) // 跨环境获取全局对象
// 动态 import —— 已在模块化章节介绍
// import.meta
console.log(import.meta.url) // 当前模块的 URLES2021
js
// String.prototype.replaceAll
'hello world'.replaceAll('o', '0') // 'hell0 w0rld'
'aabbcc'.replaceAll(/b/g, 'x') // 'aaxxcc'
// Promise.any(最快的成功结果)
const first = await Promise.any([
fetch('/api/a'),
fetch('/api/b'),
fetch('/api/c'),
])
// AggregateError:全部失败时抛出
// 逻辑赋值运算符
let count = 0
let username = null
let fallback = undefined
count ||= 5 // count = count || 5 → count = 5(count 是假值时赋值)
count &&= 10 // count = count && 10 → count = 10(count 是真值时赋值)
username ??= '匿名' // username = username ?? '匿名' → username = '匿名'(username 是 null/undefined 时赋值)
fallback ??= 'ok' // fallback = 'ok'
// 数值分隔符(提高大数字可读性)
const billion = 1_000_000_000
const bytes = 0xFF_FF_FF_FF
const binary = 0b1010_0001_1000_0101
const hex = 0xA0_B0_C0
// WeakRef(弱引用)
// 什么是弱引用?正常情况下,只要变量引用了一个对象,这个对象就不会被垃圾回收。
// 弱引用不会阻止垃圾回收,当原对象没有其他引用时,垃圾回收器会回收它。
// 什么时候用弱引用?
// 1. 缓存大对象:缓存可能被回收,下次访问时重新计算
// 2. 避免内存泄漏:DOM 节点被移除后,引用也能自动失效
// 3. 追踪第三方库实例:不阻止它们被回收
let obj = { data: 'large' }
const ref = new WeakRef(obj)
obj = null // 原对象可被垃圾回收
// ref.deref() 可能返回 undefined(已被回收)或原对象
// FinalizationRegistry(垃圾回收回调)
// 什么时候用?当对象被垃圾回收时,执行清理操作(如关闭连接、释放资源)
const registry = new FinalizationRegistry((heldValue) => {
console.log(`${heldValue} 被回收了`)
})
registry.register(someObject, 'myObject')ES2022
js
// Array.prototype.at(负数索引)
const arr = [1, 2, 3, 4, 5]
arr.at(0) // 1
arr.at(-1) // 5
arr.at(-2) // 4
// Object.hasOwn(替代 hasOwnProperty)
const obj = { a: 1 }
Object.hasOwn(obj, 'a') // true
Object.hasOwn(obj, 'b') // false
Object.hasOwn({}, 'toString') // false(原型上的不算)
// Error.cause(错误链)
try {
// ...
} catch (err) {
throw new Error('包装错误', { cause: err })
}
// 正则 /d 标志(返回匹配的开始和结束索引)
const result = 'hello'.match(/l/dg)
// result.indices: [[2, 3], [3, 4]]
// 顶层 await(在模块顶层直接使用 await)
// 只在 ES Module 中有效
const data = await fetch('/api/data').then((r) => r.json())
export { data }
// class 字段声明
class Person {
// 公有字段
name = '张三'
// 私有字段(外部不可访问)
#age = 25
// 私有方法
#validate() {
return this.#age > 0
}
// 静态公有字段
static count = 0
// 静态私有字段
static #total = 0
// 静态块(初始化复杂静态属性)
static {
// 可以访问私有字段
Person.#total = 100
}
getAge() {
return this.#age
}
}
const person = new Person()
console.log(person.name) // '张三'
// console.log(person.#age) // ❌ SyntaxError
console.log(person.getAge()) // 25ES2023
js
// Array.prototype.findLast / findLastIndex(从后往前找)
const arr = [1, 2, 3, 4, 5]
arr.find((x) => x > 3) // 4(从前往后,第一个)
arr.findLast((x) => x > 3) // 5(从后往前,第一个)
arr.findIndex((x) => x > 3) // 3(从前往后)
arr.findLastIndex((x) => x > 3) // 4(从后往前)
// Array.prototype.toSorted / toReversed / toSpliced / with(ES2023,不修改原数组)
const nums = [3, 1, 4, 1, 5]
// toSorted:返回排序后的新数组(不修改原数组)
const sorted = nums.toSorted((a, b) => a - b) // [1, 1, 3, 4, 5]
console.log(nums) // [3, 1, 4, 1, 5](原数组不变)
// toReversed:返回反转后的新数组
const reversed = nums.toReversed() // [5, 1, 4, 1, 3]
console.log(nums) // [3, 1, 4, 1, 5](原数组不变)
// toSpliced:返回增删后的新数组(替代 splice)
const letters = ['a', 'b', 'c', 'd']
const spliced = letters.toSpliced(1, 2, 'x', 'y') // ['a', 'x', 'y', 'd']
console.log(letters) // ['a', 'b', 'c', 'd'](原数组不变)
// with:返回替换指定索引后的新数组
const items = [1, 2, 3, 4, 5]
const updated = items.with(2, 99) // [1, 2, 99, 4, 5]
console.log(items) // [1, 2, 3, 4, 5](原数组不变)
// 💡 为什么要用这些方法?
// Vue 3 的响应式系统依赖引用变化来触发更新
// 直接修改原数组(sort/reverse/splice)可能不会触发响应式更新
// 使用 toSorted/toReversed 等返回新数组的方式更安全
const list = ref([3, 1, 2])
list.value = list.value.toSorted() // ✅ 触发响应式更新ES2024
js
// Symbol.dispose / Symbol.asyncDispose(显式资源管理)
// using 关键字自动在代码块结束时调用清理函数,类似 C# 的 using 或 Python 的 with
class FileWriter {
#path
#handle
constructor(path) {
this.#path = path
this.#handle = openFile(path)
}
write(data) {
this.#handle.write(data)
}
// 同步释放
[Symbol.dispose]() {
this.#handle.close()
console.log(`${this.#path} 已关闭`)
}
}
// using:离开作用域时自动调用 Symbol.dispose
{
using file = new FileWriter('output.txt')
file.write('hello')
} // 自动关闭文件
// await using:异步释放资源,配合 Symbol.asyncDispose
class DatabaseConnection {
#connection
constructor(url) { this.#connection = connect(url) }
query(sql) { return this.#connection.execute(sql) }
[Symbol.dispose]() { this.#connection.close() }
async [Symbol.asyncDispose]() { await this.#connection.closeAsync() }
}
{
await using db = new DatabaseConnection('postgres://...')
await db.query('SELECT 1')
} // 自动异步关闭连接
// Object.groupBy / Map.groupBy(数组分组)
const people = [
{ name: '张三', age: 25 },
{ name: '李四', age: 30 },
{ name: '王五', age: 25 },
]
const grouped = Object.groupBy(people, (person) => person.age)
// {
// 25: [{ name: '张三', age: 25 }, { name: '王五', age: 25 }],
// 30: [{ name: '李四', age: 30 }]
// }
// Promise.withResolvers(创建 Promise 的便捷方式)
const { promise, resolve, reject } = Promise.withResolvers()
setTimeout(() => resolve('done'), 1000)
const result = await promise
// String.prototype.isWellFormed / toWellFormed(ES2024,处理 Unicode 字符串)
// 什么是"格式良好"的字符串?不包含孤立代理项(lone surrogate)的字符串
// 代理项是 UTF-16 编码中用于表示超出 BMP 范围字符的特殊码元
// 孤立代理项示例
const bad = 'hello\uD800world' // 包含孤立的高位代理项
bad.isWellFormed() // false
// isWellFormed:检查字符串是否格式良好
'hello'.isWellFormed() // true
'hello\uD800world'.isWellFormed() // false
// toWellFormed:将孤立代理项替换为 U+FFFD(替换字符)
'hello\uD800world'.toWellFormed() // 'hello�world'
// 实际应用:处理用户输入或外部数据时的安全检查
function safeString(str) {
return str.isWellFormed() ? str : str.toWellFormed()
}
// Array.fromAsync(ES2024,从异步可迭代对象创建数组)
// 类似 Array.from,但支持异步迭代器和 Promise 数组
// 从 Promise 数组创建
const promises = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]
const results = await Array.fromAsync(promises) // [1, 2, 3]
// 从异步迭代器创建
async function* generate() {
yield 1
yield 2
yield 3
}
const arr = await Array.fromAsync(generate()) // [1, 2, 3]
// 带 mapFn(第二个参数)
const doubled = await Array.fromAsync([Promise.resolve(1), Promise.resolve(2)], (x) => x * 2)
// [2, 4]
// 错误处理:如果某个 Promise 失败,会抛出错误
try {
await Array.fromAsync([Promise.resolve(1), Promise.reject(new Error('fail'))])
} catch (err) {
console.error(err) // Error: fail
}版本特性速查表
| 版本 | 重要特性 |
|---|---|
| ES2015 (ES6) | let/const、箭头函数、解构、模板字符串、Promise、class、Module、Symbol、Map/Set |
| ES2016 | Array.includes、** 指数运算符 |
| ES2017 | async/await、Object.entries/values、padStart/padEnd、Object.getOwnPropertyDescriptors、尾逗号 |
| ES2018 | 对象展开/剩余、Promise.finally、正则命名捕获组、for-await-of |
| ES2019 | Array.flat/flatMap、Object.fromEntries、trimStart/trimEnd、可选 catch 绑定 |
| ES2020 | 可选链 ?.、空值合并 ??、BigInt、Promise.allSettled、globalThis、matchAll、动态 import |
| ES2021 | replaceAll、Promise.any、逻辑赋值 ` |
| ES2022 | Array.at、Object.hasOwn、Error.cause、顶层 await、class 私有字段/方法、正则 /d 标志 |
| ES2023 | Array.findLast/findLastIndex、toSorted/toReversed/toSpliced/with |
| ES2024 | Object.groupBy、Promise.withResolvers、Symbol.dispose/using 资源管理、isWellFormed/toWellFormed、Array.fromAsync |
Vue 项目中的 ES6+ 实际应用
js
// ---- 解构:组合式 API 中提取响应式数据 ----
import { ref, computed } from 'vue'
const { count, increment } = useCounter() // 自定义 Hook 返回解构
// ---- 展开运算符:透传 Props ----
const props = defineProps({ name: String, age: Number })
const extraProps = { ...props, role: 'admin' }
// ---- 可选链 + 空值合并:安全访问嵌套数据 ----
const cityName = user?.address?.city ?? '未知城市'
// ---- Promise.all:并行请求 ----
const [userRes, orderRes] = await Promise.all([
fetch('/api/user'),
fetch('/api/orders'),
])
// ---- Map/Set:去重和快速查找 ----
const selectedIds = new Set([1, 2, 3])
const isSelected = (id) => selectedIds.has(id) // O(1) 查找
// ---- 模板字符串:动态 class/style ----
const btnClass = `btn btn-${variant ?? 'default'} btn-${size ?? 'md'}`
// ---- Object.entries:遍历配置对象 ----
const themeVars = { '--primary': '#1890ff', '--radius': '4px' }
Object.entries(themeVars).forEach(([key, value]) => {
document.documentElement.style.setProperty(key, value)
})