解构赋值

张玥 2026年9月16日 阅读时间 20分钟
ES6+ 解构赋值 JavaScript 数组解构 对象解构
ES6+新特性之解构赋值

解构赋值(Destructuring Assignment)是 ES6 中最实用、使用频率最高的语法特性之一。它允许我们从数组或对象中"拆解"出值,并优雅地赋值给变量,让原本冗长繁琐的属性访问代码瞬间变得简洁清晰。本文将从基础语法出发,系统讲解数组解构、对象解构、默认值、嵌套解构、剩余元素、函数参数解构、实际应用场景以及常见陷阱,帮助你彻底掌握这一现代 JavaScript 的核心利器。20 分钟沉浸式学习,让你的代码从此告别重复的 obj.a && obj.a.b。

为什么需要解构赋值

在 ES6 之前,我们要从一个对象中取出几个属性,往往需要重复写很多次对象名;要从数组中取值,也只能通过下标逐个访问。解构赋值正是为了解决这类"样板代码"而诞生的语法糖。

/* 传统写法:重复且冗长 */
var user = { name: '张玥', age: 28, city: '北京' };
var name = user.name;
var age = user.age;
var city = user.city;

/* 数组取值:只能靠下标 */
var arr = [1, 2, 3];
var first = arr[0];
var second = arr[1];

/* ES6 解构:一行搞定 */
const { name, age, city } = user;
const [first, second] = arr;
更少的代码
一次声明多个变量,无需重复对象名
更强的可读性
一眼看出"我需要哪些数据"
更安全的取值
配合默认值,避免 undefined 蔓延

数组解构:按位置取值

数组解构按照索引位置进行匹配,左边的变量依次对应右边数组中的元素。

/* 基本用法 */
const [a, b, c] = [1, 2, 3];
// a = 1, b = 2, c = 3

/* 跳过某些元素(用逗号占位) */
const [, , third] = [1, 2, 3];
// third = 3

/* 元素少于变量:多出的为 undefined */
const [x, y, z] = [1, 2];
// x=1, y=2, z=undefined

/* 元素多于变量:多余的被忽略 */
const [m, n] = [1, 2, 3];
// m = 1, n = 2
1
2
3
a
b
c

数组解构是"按位置"一一对应

对象解构:按属性名取值

对象解构依据属性名进行匹配,与顺序无关。这是日常开发中使用最多的一种解构形式。

/* 基本用法:变量名 = 属性名 */
const person = { name: '张玥', age: 28 };
const { name, age } = person;
// name = '张玥', age = 28

/* 重命名:属性名: 新变量名 */
const { name: userName, age: userAge } = person;
// userName = '张玥', userAge = 28

/* 顺序无关 */
const { age, name } = person;
// 结果完全相同

/* 直接解构函数返回值 */
const { data, status } = await fetch('/api/user');
person = {
name: '张玥',
age: 28
}
name age

对象解构是"按属性名"匹配

默认值:处理 undefined 的利器

当解构出来的值是 undefined 时,可以为其指定默认值。这是解构赋值最实用的能力之一。

/* 数组解构默认值 */
const [a = 1, b = 2] = [10];
// a = 10, b = 2

/* 对象解构默认值 */
const { name = '匿名', age = 18 } = { age: 28 };
// name = '匿名', age = 28

/* 重要:只有 undefined 才触发默认值 */
const { x = 100 } = { x: null };
// x = null(不是 100!)

/* 默认值可以是表达式或函数调用 */
const { list = [] } = options;
const { id = generateId() } = config;
undefined
{ a } = {} → a 使用默认值 ✅
null
{ a } = { a: null } → a = null ❌
0 / '' / false
{ a } = { a: 0 } → a = 0 ❌

默认值仅在值为 undefined 时生效

嵌套解构:深入多层结构

当数据结构存在多层嵌套时,解构可以层层深入,直接取出深层属性,避免出现 a && a.b && a.b.c 这样的防御式代码。

/* 嵌套对象解构 */
const user = {
  name: '张玥',
  address: {
    city: '北京',
    district: '海淀区'
  }
};

const { address: { city, district } } = user;
// city = '北京', district = '海淀区'

/* 嵌套数组 + 对象混合 */
const { list: [firstItem, secondItem] } = response;

/* 深层默认值保护 */
const { profile: { nickname = '未设置' } = {} } = user;
user
├─ name: '张玥'
└─ address
├─ city: '北京'
└─ district: '海淀区'
const { address: { city } } = user;

像路径一样精准定位深层数据

函数参数解构

在函数参数位置直接解构,是提升函数可读性的绝佳手段,尤其适用于配置对象参数。它与"命名参数"的效果类似,却不需要额外语法支持。

/* 传统写法:参数顺序难以记忆 */
function createUser(name, age, city, role) { }
createUser('张玥', 28, '北京', 'admin');

/* 解构写法:命名参数,清晰直观 */
function createUser({ name, age, city = '北京', role = 'user' }) {
  return { name, age, city, role };
}

createUser({ name: '张玥', age: 28, role: 'admin' });

/* 注意:必须给整个参数对象一个默认值 */
function init({ debug = false } = {}) { }
init(); // 不传参也不会报错
❌ 位置参数
createUser('张玥', 28, '北京', 'admin')
顺序不能错,参数多了记不住
✅ 解构参数
createUser({ name, age, role })
顺序无关,自解释,支持默认值

交换变量与返回多值

解构赋值解决了一些长期以来的编码痛点,比如交换两个变量、让函数返回多个值。

/* 交换变量:告别中间变量 */
let a = 1, b = 2;
[a, b] = [b, a];
// a = 2, b = 1

/* 函数返回多个值 */
function getMinMax(arr) {
  return [Math.min(...arr), Math.max(...arr)];
}
const [min, max] = getMinMax([3, 1, 9]);

/* 返回对象更推荐:可读性更强 */
function parseQuery(str) {
  return { key, value };
}
const { key, value } = parseQuery('a=1');
a=1
b=2
a=2
b=1

[a, b] = [b, a] 一行完成交换

剩余元素(Rest)

使用 ... 语法可以把剩余的元素收集到一个数组或对象中,常与解构配合使用,实现"取一部分,留一部分"。

/* 数组剩余元素 */
const [first, ...rest] = [1, 2, 3, 4];
// first = 1, rest = [2, 3, 4]

/* 对象剩余属性(ES2018) */
const { id, ...others } = { id: 1, a: 2, b: 3 };
// id = 1, others = { a: 2, b: 3 }

/* 常用于剔除敏感字段 */
const { password, ...safeUser } = dbUser;

/* 注意:剩余元素必须在最后 */
const [...head, last] = arr; // ❌ 语法错误
原始对象
password id name email
const { password, ...safeUser } = user;
safeUser 中已不含 password

解构可迭代对象

数组解构本质上依赖的是 Symbol.iterator 迭代器协议,因此任何可迭代对象(字符串、Set、Map、生成器等)都可以被解构。

/* 字符串解构 */
const [c1, c2, c3] = 'ABC';
// c1='A', c2='B', c3='C'

/* Set 解构 */
const [p, q] = new Set([1, 2, 3]);

/* Map 解构 */
const map = new Map([['a', 1], ['b', 2]]);
for (const [k, v] of map) {
  console.log(k, v);
}

/* 自定义可迭代对象 */
const iterable = {
  *[Symbol.iterator]() {
    yield 1; yield 2; yield 3;
  }
};
const [one, two] = iterable;

String

Set

Map

Generator

只要实现了 Symbol.iterator,就能被解构

实战应用场景

解构赋值在现代前端开发中无处不在。掌握以下典型场景,能显著提升代码质量。

/* 1. 解构 API 响应 */
const { data: { list = [], total = 0 } } = res;

/* 2. 模块导入时解构 */
import { useState, useEffect } from 'react';

/* 3. 解构 props */
function Card({ title, content, onClose }) {
  return <div>{title}</div>;
}

/* 4. 数组解构配合迭代 */
for (const [index, item] of list.entries()) {
  render(index, item);
}

/* 5. 正则匹配结果解构 */
const [, year, month, day] = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
接口数据 — 层层解构,直取所需
模块化 — import 解构一目了然
组件开发 — props 解构更清晰
数据处理 — 遍历、正则、元组

常见陷阱与注意事项

解构虽好,但也有一些容易踩坑的细节,稍不注意就会产生意外结果。

/* 陷阱1:声明语句不能以 { 开头 */
{ a, b } = obj; // ❌ 被当成代码块
({ a, b } = obj); // ✅ 加括号

/* 陷阱2:对象解构给已声明变量 */
let name;
({ name } = user); // ✅ 必须加括号

/* 陷阱3:undefined 会抛错 */
const { a } = null; // ❌ TypeError
const { a } = undefined; // ❌ TypeError

/* 陷阱4:嵌套解构的中间层不存在 */
const { x: { y } } = {}; // ❌ 报错
const { x: { y } = {} } = {}; // ✅
括号问题
({ a } = obj) 不能省略括号
null / undefined
解构前确保数据源存在
深层路径
嵌套解构要给中间层默认值 {}

最佳实践与总结

  • 按需解构:只取出真正需要的属性,避免全量解构造成语义模糊
  • 善用默认值:为可选参数、接口字段提供默认值,减少防御式判断
  • 适度嵌套:嵌套过深会降低可读性,两层以上建议分步解构
  • 函数参数优先用对象解构:命名参数比位置参数更易维护
  • 剩余元素用于剔除字段:如 const { password, ...rest } = user
  • 注意 null 与 undefined:解构不会自动兜底,必要时先做保护
  • 保持命名语义化:重命名时选择更贴合上下文的变量名
/* 解构赋值的黄金法则 */
/* 取之有道 → 命名清晰 → 兜底有方 */

/* 一个完整的实战示例 */
function renderUser(
  {
    name = '匿名用户',
    age,
    address: { city = '未知城市' } = {},
    tags = []
  } = {}
) {
  return { name, age, city, tagCount: tags.length };
}