Skip to content

ES6+ 现代特性

ES6(ES2015)是 JavaScript 的分水岭,此后每年发布新特性。掌握这些特性是写出现代 JS 的基础。

解构赋值

js
// 数组解构
const [a, b, ...rest] = [1, 2, 3, 4, 5]
// a=1, b=2, rest=[3,4,5]

// 跳过元素
const [,, third] = [1, 2, 3]  // third=3

// 默认值
const [x = 10, y = 20] = [5]  // x=5, y=20

// 对象解构
const { name, age, address: { city } } = {
  name: 'Alice',
  age: 30,
  address: { city: 'Beijing' }
}

// 重命名 + 默认值
const { name: userName = 'Anonymous', role = 'user' } = { name: 'Bob' }
// userName='Bob', role='user'

// 函数参数解构
function render({ title, content, theme = 'light' }) {
  return `<div class="${theme}"><h1>${title}</h1>${content}</div>`
}

展开运算符 & 剩余参数

js
// 展开数组
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const merged = [...arr1, ...arr2]  // [1,2,3,4,5,6]

// 展开对象(浅拷贝)
const defaults = { theme: 'light', lang: 'zh' }
const config = { ...defaults, theme: 'dark', debug: true }
// { theme: 'dark', lang: 'zh', debug: true }

// 剩余参数
function sum(first, ...rest) {
  return rest.reduce((acc, n) => acc + n, first)
}
sum(1, 2, 3, 4)  // 10

// 克隆数组/对象(浅拷贝)
const clone = [...original]
const objClone = { ...original }

模板字符串

js
const name = 'World'
const multiline = `
  Hello, ${name}!
  Today is ${new Date().toLocaleDateString()}
  2 + 2 = ${2 + 2}
`

// 标签模板(Tagged Template)
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    const value = values[i - 1]
    return result + (value ? `<mark>${value}</mark>` : '') + str
  })
}

const keyword = 'JavaScript'
highlight`学习 ${keyword} 很有趣`
// '学习 <mark>JavaScript</mark> 很有趣'

// 实际应用:SQL 防注入
function sql(strings, ...values) {
  const sanitized = values.map(v => escape(v))
  return strings.reduce((q, s, i) => q + (sanitized[i-1] ?? '') + s)
}

可选链 & 空值合并(ES2020)

js
const user = {
  profile: {
    address: null
  }
}

// ❌ 老写法:繁琐
const city = user && user.profile && user.profile.address && user.profile.address.city

// ✅ 可选链
const city = user?.profile?.address?.city  // undefined(不报错)

// 方法调用
user?.profile?.getAvatar?.()

// 数组访问
const first = arr?.[0]

// 空值合并:只有 null/undefined 才用默认值(区别于 ||)
const port = config.port ?? 3000   // config.port 为 0 时,用 0 而非 3000
const name = user.name || 'Guest'  // user.name 为 '' 时,用 'Guest'(可能不是你想要的)
const name2 = user.name ?? 'Guest' // user.name 为 '' 时,用 ''

// 赋值简写(ES2021)
user.name ??= 'Anonymous'   // 只有 null/undefined 时才赋值
user.count ||= 0            // falsy 时赋值
user.count &&= user.count + 1  // truthy 时赋值

Symbol

js
// Symbol 是唯一的原始值
const id1 = Symbol('id')
const id2 = Symbol('id')
console.log(id1 === id2)  // false

// 用作对象的唯一键(不会与其他属性冲突)
const ID = Symbol('id')
const user = {
  [ID]: 123,
  name: 'Alice'
}
console.log(user[ID])  // 123
// Symbol 键不会出现在 for...in 和 Object.keys() 中

// 内置 Symbol(Well-known Symbols)
class MyArray {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance)
  }
}
console.log([] instanceof MyArray)  // true

// 自定义迭代器
class Range {
  constructor(start, end) {
    this.start = start
    this.end = end
  }

  [Symbol.iterator]() {
    let current = this.start
    const end = this.end
    return {
      next() {
        return current <= end
          ? { value: current++, done: false }
          : { done: true }
      }
    }
  }
}

for (const n of new Range(1, 5)) {
  console.log(n)  // 1 2 3 4 5
}

Map & Set

js
// Map:键可以是任意类型
const map = new Map()
const keyObj = { id: 1 }

map.set(keyObj, 'Alice')
map.set('name', 'Bob')
map.set(42, 'Charlie')

map.get(keyObj)  // 'Alice'
map.size         // 3

// 遍历
for (const [key, value] of map) {
  console.log(key, value)
}

// 对象转 Map
const obj = { a: 1, b: 2 }
const mapFromObj = new Map(Object.entries(obj))

// Set:唯一值集合
const set = new Set([1, 2, 3, 2, 1])
console.log([...set])  // [1, 2, 3]

// 数组去重
const unique = [...new Set(arr)]

// 集合运算
const a = new Set([1, 2, 3, 4])
const b = new Set([3, 4, 5, 6])

const union        = new Set([...a, ...b])          // 并集
const intersection = new Set([...a].filter(x => b.has(x)))  // 交集
const difference   = new Set([...a].filter(x => !b.has(x))) // 差集

常用 ES2022+ 特性

js
// Array.at():支持负索引
const arr = [1, 2, 3, 4, 5]
arr.at(-1)   // 5(最后一个)
arr.at(-2)   // 4

// Object.hasOwn():替代 hasOwnProperty
Object.hasOwn(obj, 'key')  // 更安全,不受原型链影响

// Error cause(ES2022)
try {
  await fetchData()
} catch (err) {
  throw new Error('数据加载失败', { cause: err })
}

// Array.findLast / findLastIndex(ES2023)
const last = arr.findLast(x => x % 2 === 0)

// Object.groupBy(ES2024)
const people = [
  { name: 'Alice', dept: 'Engineering' },
  { name: 'Bob',   dept: 'Marketing' },
  { name: 'Carol', dept: 'Engineering' },
]
const byDept = Object.groupBy(people, p => p.dept)
// { Engineering: [...], Marketing: [...] }

总结

  • 解构 + 展开运算符让数据操作更简洁
  • 可选链 ?. 和空值合并 ?? 是处理 null/undefined 的利器
  • Map/Set 在需要非字符串键或唯一值时优于普通对象/数组
  • Symbol 用于创建唯一标识符和自定义对象行为
  • 每年跟进 ECMAScript 新特性,善用 MDN 查兼容性

系统学习 Web 前端生态,深入底层架构