Skip to content

解构赋值与其他 ES6 特性面试题

一、核心要点速览

💡 核心考点

  • 解构赋值: 从数组/对象中提取数据
  • 模板字符串: 多行字符串与插值语法
  • 剩余参数: ...args 收集剩余参数
  • 扩展运算符: ...array 展开数组/对象
  • Symbol: 唯一标识符

二、数组解构

1. 基础用法

javascript
// 基本解构
const [a, b, c] = [1, 2, 3]
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3

// 跳过某些值
const [first, , third] = [1, 2, 3]
console.log(first) // 1
console.log(third) // 3

// 默认值
const [x = 1, y = 2] = [undefined]
console.log(x) // 1(默认值生效)
console.log(y) // 2(默认值生效)

// 只在值为 undefined 时使用默认值
const [a = 1, b = 2] = [null, 0]
console.log(a) // null(不是 undefined)
console.log(b) // 0(不是 undefined)

2. 剩余运算符

javascript
// 收集剩余元素
const [head, ...tail] = [1, 2, 3, 4]
console.log(head) // 1
console.log(tail) // [2, 3, 4]

// 交换变量
let a = 1, b = 2
[a, b] = [b, a]
console.log(a) // 2
console.log(b) // 1

// 嵌套解构
const [1, [2, 3]] = [1, [2, 3]]
const [x, [y, z]] = [1, [2, 3]]
console.log(x) // 1
console.log(y) // 2
console.log(z) // 3

3. 函数参数解构

javascript
// 数组参数解构
function printPair([first, second]) {
  console.log(first, second)
}

printPair([1, 2]) // 1 2

// 带默认值
function multiply([a = 1, b = 1] = []) {
  return a * b
}

multiply()        // 1(使用默认参数和默认值)
multiply([2, 3])  // 6
multiply([5])     // 5

三、对象解构

1. 基础用法

javascript
// 基本解构
const { name, age } = { name: 'Vue', age: 3 }
console.log(name) // 'Vue'
console.log(age)  // 3

// 重命名
const { name: userName, age: userAge } = { name: 'Vue', age: 3 }
console.log(userName) // 'Vue'
console.log(userAge)  // 3

// 默认值
const { count = 0, enabled = true } = {}
console.log(count)   // 0
console.log(enabled) // true

// 先声明后解构
let x, y
;({ x, y } = { x: 1, y: 2 }) // 注意括号

2. 嵌套解构

javascript
// 嵌套对象
const { 
  user: { 
    name, 
    profile: { email } 
  } 
} = {
  user: {
    name: 'Vue',
    profile: {
      email: '[email protected]'
    }
  }
}

console.log(name)  // 'Vue'
console.log(email) // '[email protected]'

// 设置默认值
const { 
  user: { name = 'Anonymous' } = {} 
} = {}

console.log(name) // 'Anonymous'

3. 函数参数解构

javascript
// 对象参数解构
function printUser({ name, age }) {
  console.log(`${name}, ${age}`)
}

printUser({ name: 'Vue', age: 3 }) // Vue, 3

// 重命名 + 默认值
function createUser({ 
  name = 'Guest', 
  email = '[email protected]',
  role = 'user' 
} = {}) {
  return { name, email, role }
}

createUser() // { name: 'Guest', email: '[email protected]', role: 'user' }

// 实际应用:配置对象
function fetch(url, { 
  method = 'GET',
  headers = {},
  timeout = 5000,
  retry = 3
} = {}) {
  // ...
}

四、实际应用场景

1. 函数返回多个值

javascript
// 返回对象
function getStats() {
  return { 
    success: 200, 
    failed: 5,
    total: 205 
  }
}

const { success, failed, total } = getStats()

// 返回数组
function getMinMax(arr) {
  return [Math.min(...arr), Math.max(...arr)]
}

const [min, max] = getMinMax([3, 1, 4, 1, 5])
console.log(min) // 1
console.log(max) // 5

2. 遍历 Map

javascript
const map = new Map([
  ['a', 1],
  ['b', 2],
  ['c', 3]
])

// 解构键值对
for (const [key, value] of map) {
  console.log(key, value)
}

// forEach 中也可以
map.forEach((value, key) => {
  console.log(key, value)
})

3. 提取 JSON 数据

javascript
// 从 API 响应中提取
async function getUser(id) {
  const response = await fetch(`/api/users/${id}`)
  const { data: { name, email }, meta: { timestamp } } = await response.json()
  
  return { name, email, timestamp }
}

// 简化写法
async function getUser(id) {
  const { data: { name, email } } = await fetch(`/api/users/${id}`).then(r => r.json())
  return { name, email }
}

五、模板字符串

1. 基础用法

javascript
const name = 'Vue'
const version = 3

// 插值
const greeting = `Hello, ${name}!`
console.log(greeting) // Hello, Vue!

// 表达式
const result = `结果:${1 + 2} = ${3}`

// 多行字符串
const html = `
  <div class="container">
    <h1>${name}</h1>
    <p>Version ${version}</p>
  </div>
`

// 普通字符串对比
const multiLine = '第一行\n' +
                  '第二行\n' +
                  '第三行'

const multiLineES6 = `第一行
第二行
第三行`

2. 标签模板

javascript
// 标签函数
function tag(strings, ...values) {
  console.log(strings) // ['Hello ', '']
  console.log(values)  // ['World']
  
  return strings[0] + values[0].toUpperCase() + strings[1]
}

const result = tag`Hello ${'World'}!`
console.log(result) // Hello WORLD!

// 实际应用:转义 HTML
function escapeHtml(strings, ...values) {
  const escape = str => str.replace(/&/g, '&amp;')
                           .replace(/</g, '&lt;')
                           .replace(/>/g, '&gt;')
  
  let result = ''
  values.forEach((val, i) => {
    result += strings[i] + escape(String(val))
  })
  result += strings[strings.length - 1]
  
  return result
}

const userInput = '<script>alert("XSS")</script>'
const safe = escapeHtml`<div>${userInput}</div>`
// <div>&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;</div>

3. String.raw

javascript
// 获取原始字符串(不处理转义)
const path = String.raw`C:\Users\Name\Documents`
console.log(path) // C:\Users\Name\Documents

// 对比
const normal = `C:\\Users\\Name\\Documents`
console.log(normal) // C:\Users\Name\Documents

六、剩余参数与扩展运算符

1. 剩余参数 (...args)

javascript
// 函数参数
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0)
}

sum(1, 2, 3, 4) // 10

// 必须放在最后
function fn(required, optional = 1, ...rest) {
  console.log(required) // 必需参数
  console.log(optional) // 可选参数
  console.log(rest)     // 剩余参数
}

fn(1, 2, 3, 4, 5) // 1, 2, [3, 4, 5]

// 解构中的剩余参数
const [first, second, ...others] = [1, 2, 3, 4, 5]
console.log(first)  // 1
console.log(second) // 2
console.log(others) // [3, 4, 5]

2. 扩展运算符 (...spread)

javascript
// 数组展开
const arr1 = [1, 2]
const arr2 = [...arr1, 3, 4]
console.log(arr2) // [1, 2, 3, 4]

// 数组合并
const merged = [...arr1, ...arr2]
console.log(merged) // [1, 2, 1, 2, 3, 4]

// 复制数组
const copy = [...arr1]
console.log(copy) // [1, 2]

// 对象展开
const obj1 = { a: 1, b: 2 }
const obj2 = { ...obj1, c: 3 }
console.log(obj2) // { a: 1, b: 2, c: 3 }

// 对象合并
const merged = { ...obj1, ...obj2 }
console.log(merged) // { a: 1, b: 2, c: 3 }

// 注意:后面的属性覆盖前面的
const override = { ...obj1, b: 99 }
console.log(override) // { a: 1, b: 99 }

3. 实际应用

javascript
// 不可变更新
function updateUser(user, updates) {
  return { ...user, ...updates }
}

const user = { name: 'Vue', age: 3 }
const updated = updateUser(user, { age: 4 })
console.log(updated) // { name: 'Vue', age: 4 }

// 数组转 Set(去重)
const unique = [...new Set([1, 2, 2, 3, 3, 3])]
console.log(unique) // [1, 2, 3]

// Set 转数组
const arr = [...new Set([1, 2, 3])]

// 字符串转数组
const chars = [...'Hello']
console.log(chars) // ['H', 'e', 'l', 'l', 'o']

七、Symbol

1. 创建与特性

javascript
// 创建唯一值
const id1 = Symbol('id')
const id2 = Symbol('id')

console.log(id1 === id2) // false

// Symbol 总是唯一的,即使描述相同
const sym1 = Symbol()
const sym2 = Symbol()

console.log(typeof sym1) // 'symbol'
console.log(sym1.toString()) // 'Symbol()'

2. 作为对象属性

javascript
// 隐藏属性
const obj = {
  [Symbol('id')]: 1,
  name: 'Vue'
}

console.log(obj.name) // 'Vue'
console.log(Object.keys(obj)) // ['name'](不包含 Symbol)
console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(id)]

// 访问 Symbol 属性
const symbols = Object.getOwnPropertySymbols(obj)
console.log(obj[symbols[0]]) // 1

3. 全局共享 Symbol

javascript
// 全局注册
const shared1 = Symbol.for('shared')
const shared2 = Symbol.for('shared')

console.log(shared1 === shared2) // true

// 获取描述
const key = Symbol.keyFor(shared1)
console.log(key) // 'shared'

// 应用:Well-Known Symbols
const iterable = {
  [Symbol.iterator]() {
    let step = 0
    return {
      next() {
        if (step < 3) {
          return { value: step++, done: false }
        }
        return { value: undefined, done: true }
      }
    }
  }
}

for (const item of iterable) {
  console.log(item) // 0, 1, 2
}

八、面试标准回答

解构赋值是 ES6 引入的语法特性,允许从数组或对象中提取数据并赋值给变量。

数组解构按照位置对应,可以跳过某些值、设置默认值、使用剩余运算符收集剩余元素。还支持嵌套解构和交换变量。

对象解构按照属性名匹配,可以重命名、设置默认值。在函数参数中非常有用,可以替代配置对象模式。

实际应用场景包括:

  1. 函数返回多个值
  2. 遍历 Map 时的键值对提取
  3. 从 API 响应中提取嵌套数据
  4. 函数参数的简化

模板字符串提供了更强大的字符串功能:

  • 多行字符串无需拼接
  • 插值语法 ${expression}
  • 标签模板实现自定义处理

剩余参数和扩展运算符是两个重要特性:

  • 剩余参数(...args)收集剩余元素
  • 扩展运算符(...array)展开数组或对象
  • 支持不可变数据更新

Symbol 是唯一标识符,主要用于:

  • 创建隐藏属性
  • 避免属性名冲突
  • 实现迭代器协议

实际项目中,我大量使用解构赋值简化代码,用模板字符串处理文本,用扩展运算符实现不可变更新。


九、记忆口诀

解构赋值歌诀:

数组解构按位置,
对象解构按名称。
默认值来保底,
剩余参数收末尾!

模板字符串强大,
多行插值都用它。
标签模板能定制,
String.raw 保原样!

剩余扩展是一对,
一个收集一个展。
Symbol 是唯一,
隐藏属性防冲突!

十、推荐资源


十一、总结一句话

  • 解构赋值: 模式匹配 + 数据提取 = 简洁代码利器
  • 模板字符串: 多行文本 + 表达式插值 = 字符串处理神器 📝
  • 剩余扩展: 收集剩余 + 展开数组 = 函数参数必备 🎯
  • Symbol: 唯一标识 + 隐藏属性 = 避免命名冲突 🔐
最近更新