掌握了基础解构后,来看一些更复杂的场景和技巧。
嵌套解构#
嵌套数组#
const matrix = [ [1, 2], [3, 4],]const [[a, b], [c, d]] = matrixconsole.log(a, b, c, d) // 1 2 3 4
// 部分解构const [[first]] = matrixconsole.log(first) // 1嵌套对象#
const user = { name: '张三', address: { city: '北京', district: '朝阳', },}
const { address: { city, district },} = userconsole.log(city) // '北京'console.log(district) // '朝阳'// console.log(address); // ReferenceError: address 未定义🤔 如果同时需要 address 和内部属性:
const { address, address: { city },} = userconsole.log(address) // { city: '北京', district: '朝阳' }console.log(city) // '北京'混合解构#
数组和对象混合:
const data = { items: [ { id: 1, name: '商品A' }, { id: 2, name: '商品B' }, ],}
const { items: [first, second],} = dataconsole.log(first) // { id: 1, name: '商品A' }
// 更深一层const { items: [{ name: firstName }, { name: secondName }],} = dataconsole.log(firstName, secondName) // '商品A' '商品B'函数参数解构#
基本用法#
function greet({ name, age }) { console.log(`${name}, ${age}岁`)}
greet({ name: '张三', age: 25 }) // 张三, 25岁带默认值的参数解构#
function createUser({ name = '匿名', age = 18, role = 'user' } = {}) { return { name, age, role }}
createUser({ name: '张三' }) // { name: '张三', age: 18, role: 'user' }createUser() // { name: '匿名', age: 18, role: 18 }🔶 注意最后的 = {},它确保不传参数时也不会报错:
// 没有 = {} 的情况function badCreate({ name = '匿名' }) { return name}// badCreate(); // TypeError: Cannot read properties of undefined混合普通参数和解构#
function request(url, { method = 'GET', headers = {}, body } = {}) { console.log(url, method, headers, body)}
request('/api/users') // '/api/users' 'GET' {} undefinedrequest('/api/users', { method: 'POST', body: '{}' })数组参数解构#
function sum([a, b, c = 0]) { return a + b + c}
sum([1, 2]) // 3sum([1, 2, 3]) // 6
// 实用场景:处理坐标function distance([x1, y1], [x2, y2]) { return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)}
distance([0, 0], [3, 4]) // 5复杂场景实战#
API 响应处理#
// 模拟 API 响应const response = { status: 200, data: { user: { id: 1, profile: { name: '张三', avatar: 'avatar.jpg', }, }, permissions: ['read', 'write'], }, meta: { timestamp: 1699000000, },}
// 一次性提取所需数据const { data: { user: { id: userId, profile: { name: userName }, }, permissions: [primaryPermission], },} = response
console.log(userId) // 1console.log(userName) // '张三'console.log(primaryPermission) // 'read'配置对象合并#
function initApp(options = {}) { const { debug = false, api: { baseUrl = 'https://api.example.com', timeout = 5000 } = {}, ui: { theme = 'light', language = 'zh-CN' } = {}, } = options
return { debug, api: { baseUrl, timeout }, ui: { theme, language } }}
// 完全使用默认值initApp()
// 部分覆盖initApp({ debug: true, api: { timeout: 10000 },})数组操作#
// 取首尾function getFirstAndLast(arr) { const [first, ...rest] = arr const last = rest.pop() return { first, last }}
getFirstAndLast([1, 2, 3, 4]) // { first: 1, last: 4 }
// 递归处理链表结构function sum([head, ...tail]) { return head === undefined ? 0 : head + sum(tail)}
sum([1, 2, 3, 4]) // 10处理可选属性#
// 安全地处理可能不存在的嵌套属性const data = { user: { // address 可能不存在 },}
// 传统写法const city1 = data.user && data.user.address && data.user.address.city
// 解构 + 默认值const { user: { address: { city } = {} } = {} } = dataconsole.log(city) // undefined,不会报错
// 或者用可选链(更推荐)const city2 = data.user?.address?.city实用技巧#
提取对象部分属性#
// 只保留需要的属性function pick(obj, keys) { return keys.reduce((result, key) => { if (key in obj) { result[key] = obj[key] } return result }, {})}
const user = { id: 1, name: '张三', password: '123', email: 'test@test.com' }const safeUser = pick(user, ['id', 'name', 'email'])// { id: 1, name: '张三', email: 'test@test.com' }
// 用解构实现排除const { password, ...publicInfo } = user// publicInfo: { id: 1, name: '张三', email: 'test@test.com' }动态属性名解构#
const key = 'name'const obj = { name: '张三', age: 25 }
const { [key]: value } = objconsole.log(value) // '张三'
// 实际应用function getProperty(obj, propName) { const { [propName]: value } = obj return value}解构赋值的连续使用#
let a, b, cconst arr1 = [1, 2]const arr2 = [3, 4]
// 连续解构;[a, b] = arr1;[b, c] = arr2console.log(a, b, c) // 1 3 4克隆与合并#
// 浅克隆数组const original = [1, 2, 3]const [...clone] = original
// 浅克隆对象const obj = { a: 1, b: 2 }const { ...objClone } = obj
// 合并对象(覆盖同名属性)const defaults = { theme: 'light', lang: 'en' }const userSettings = { lang: 'zh' }const settings = { ...defaults, ...userSettings }// { theme: 'light', lang: 'zh' }性能考虑#
🔶 避免过深的解构#
// 不推荐:难以阅读和维护const { a: { b: { c: { d: { e }, }, }, },} = deepObject
// 推荐:分步解构或使用可选链const { a } = deepObjectconst e = a?.b?.c?.d?.e🔶 大对象解构#
解构会创建新的变量绑定,对于频繁调用的函数要注意:
// 在热路径中避免创建不必要的解构function processItem(item) { // 如果只用一次,直接访问更好 return item.value * 2}
// 多次使用时解构是值得的function processItem(item) { const { value, multiplier, offset } = item return value * multiplier + offset}常见错误#
🔶 忘记处理空值#
// 错误function getName({ user: { name } }) { return name}getName({ user: null }) // TypeError
// 正确function getName({ user: { name } = {} } = {}) { return name}getName({ user: null }) // undefined🔶 解构声明与赋值混淆#
// 声明时解构const { a } = obj
// 已有变量赋值let b;({ b } = obj) // 需要括号
// 同时声明多个不同类型const { x } = obj1const [y] = arr1// 不能写成 const { x } = obj1, [y] = arr1;小结#
嵌套解构让我们能够从复杂的数据结构中精确提取需要的部分,但要注意:
- 可读性优先:过深的嵌套解构会降低代码可读性
- 提供默认值:防止解构 undefined/null 报错
- 合理使用:不是所有场景都适合解构
- 配合可选链:
?.操作符在某些场景更简洁