解构
解构语法是 JavaScript 的一种语法,它使得从数组中解包值或从对象中解包属性到不同的变量成为可能。它可以在接收数据的位置(例如赋值的左侧或任何创建新标识符绑定的位置)使用。
试一试
let a, b, rest;
[a, b] = [10, 20];
console.log(a);
// Expected output: 10
console.log(b);
// Expected output: 20
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest);
// Expected output: Array [30, 40, 50]
语法
const [a, b] = array;
const [a, , b] = array;
const [a = aDefault, b] = array;
const [a, b, ...rest] = array;
const [a, , b, ...rest] = array;
const [a, b, ...{ pop, push }] = array;
const [a, b, ...[c, d]] = array;
const { a, b } = obj;
const { a: a1, b: b1 } = obj;
const { a: a1 = aDefault, b = bDefault } = obj;
const { a, b, ...rest } = obj;
const { a: a1, b: b1, ...rest } = obj;
const { [key]: a } = obj;
let a, b, a1, b1, c, d, rest, pop, push;
[a, b] = array;
[a, , b] = array;
[a = aDefault, b] = array;
[a, b, ...rest] = array;
[a, , b, ...rest] = array;
[a, b, ...{ pop, push }] = array;
[a, b, ...[c, d]] = array;
({ a, b } = obj); // parentheses are required
({ a: a1, b: b1 } = obj);
({ a: a1 = aDefault, b = bDefault } = obj);
({ a, b, ...rest } = obj);
({ a: a1, b: b1, ...rest } = obj);
描述
对象和数组字面量表达式提供了一种创建
const arr = [a, b, c];
解构使用类似的语法,但将其用于赋值的左侧。它通过将集合中的每个元素声明为单独的变量来执行数组声明的逆操作。
const arr = [1, 2, 3];
const [a, b, c] = arr;
// a = 1, b = 2, c = 3
至于对象,比较下面两对行,看看每对中是如何直接对应的。
const obj = { a, b, c };
const { a, b, c } = obj;
// Equivalent to:
// const a = obj.a, b = obj.b, c = obj.c;
const obj = { prop1: x, prop2: y, prop3: z };
const { prop1: x, prop2: y, prop3: z } = obj;
// Equivalent to:
// const x = obj.prop1, y = obj.prop2, z = obj.prop3;
此功能类似于 Perl 和 Python 等语言中存在的功能。
有关数组或对象解构的特定功能,请参阅下面的各个示例。
绑定和赋值
对于对象和数组解构,有两种解构模式:绑定模式和赋值模式,它们的语法略有不同。
在绑定模式中,模式以声明关键字(var
、let
或const
)开头。然后,每个单独的属性必须要么绑定到一个变量,要么进一步解构。
const obj = { a: 1, b: { c: 2 } };
const {
a,
b: { c: d },
} = obj;
// Two variables are bound: `a` and `d`
所有变量共享相同的声明,因此如果你希望某些变量可重新赋值,而另一些变量是只读的,你可能需要解构两次——一次使用let
,一次使用const
。
const obj = { a: 1, b: { c: 2 } };
const { a } = obj; // a is constant
let {
b: { c: d },
} = obj; // d is re-assignable
在许多其他语言为你绑定变量的语法中,你可以使用绑定解构模式。这包括:
for...in
、for...of
和for await...of
循环的循环变量;- 函数参数;
catch
绑定变量。
在赋值模式中,模式不以关键字开头。每个解构的属性都赋值给一个赋值目标——该目标可以事先用var
或let
声明,或者是另一个对象的属性——通常,任何可以出现在赋值表达式左侧的东西。
const numbers = [];
const obj = { a: 1, b: 2 };
({ a: numbers[0], b: numbers[1] } = obj);
// The properties `a` and `b` are assigned to properties of `numbers`
注意:当使用对象字面量解构而不带声明时,赋值语句周围的括号( ... )
是必需的。
{ a, b } = { a: 1, b: 2 }
不是有效的独立语法,因为根据表达式语句的规则,左侧的{ a, b }
被视为一个代码块而不是一个对象字面量。然而,({ a, b } = { a: 1, b: 2 })
是有效的,const { a, b } = { a: 1, b: 2 }
也是有效的。
如果你的编码风格不包含尾随分号,则( ... )
表达式前面需要一个分号,否则它可能被用于执行上一行的一个函数。
请注意,上面代码的等价
const numbers = [];
const obj = { a: 1, b: 2 };
const { a: numbers[0], b: numbers[1] } = obj;
// This is equivalent to:
// const numbers[0] = obj.a;
// const numbers[1] = obj.b;
// Which definitely is not valid.
你只能将赋值模式用作赋值运算符的左侧。你不能将它们与复合赋值运算符(如+=
或*=
)一起使用。
默认值
每个解构的属性都可以有一个undefined
时,将使用默认值。如果属性值为null
,则不使用默认值。
const [a = 1] = []; // a is 1
const { b = 2 } = { b: undefined }; // b is 2
const { c = 2 } = { c: null }; // c is null
默认值可以是任何表达式。它只会在必要时进行评估。
const { b = console.log("hey") } = { b: 2 };
// Does not log anything, because `b` is defined and there's no need
// to evaluate the default value.
剩余属性和剩余元素
你可以用剩余属性...rest
结束解构模式。对于数组解构,它将可迭代对象的剩余元素收集到一个名为rest
(或你给它的任何名称)的新数组中。对于对象解构,它将对象中所有可枚举的自有属性(尚未被解构模式取走的)复制到一个名为rest
的新对象中。
更正式地说,...rest
语法在数组解构中称为“剩余元素”,在对象解构中称为“剩余属性”,但我们通常统称它们为“剩余属性”。
const { a, ...others } = { a: 1, b: 2, c: 3 };
console.log(others); // { b: 2, c: 3 }
const [first, ...others2] = [1, 2, 3];
console.log(others2); // [2, 3]
剩余属性必须是模式中的最后一个,并且不能有尾随逗号。
const [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma
// Always consider using rest operator as the last element
示例
数组解构
基本变量赋值
const foo = ["one", "two", "three"];
const [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"
解构的元素多于源
在从赋值右侧指定长度为undefined
。
const foo = ["one", "two"];
const [red, yellow, green, blue] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // undefined
console.log(blue); // undefined
交换变量
可以在一个解构表达式中交换两个变量的值。
不使用解构,交换两个值需要一个临时变量(或者,在某些低级语言中,XOR 交换技巧)。
let a = 1;
let b = 3;
[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1
const arr = [1, 2, 3];
[arr[2], arr[1]] = [arr[1], arr[2]];
console.log(arr); // [1, 3, 2]
解析从函数返回的数组
从函数返回数组一直是可能的。解构可以使处理数组返回值更简洁。
在此示例中,f()
将其输出返回为[1, 2]
,这可以通过解构在一行中解析。
function f() {
return [1, 2];
}
const [a, b] = f();
console.log(a); // 1
console.log(b); // 2
忽略某些返回值
你可以忽略你不需要的返回值。
function f() {
return [1, 2, 3];
}
const [a, , b] = f();
console.log(a); // 1
console.log(b); // 3
const [c] = f();
console.log(c); // 1
你也可以忽略所有返回值。
[, ,] = f();
尽管在这种情况下,直接调用函数而不使用解构可能更清晰。你不必使用返回值。
使用绑定模式作为剩余属性
数组解构的剩余属性可以是另一个数组或对象绑定模式。内部解构从收集剩余元素后创建的数组中进行解构,因此你无法以这种方式访问原始可迭代对象上的任何属性。
const [a, b, ...{ length }] = [1, 2, 3];
console.log(a, b, length); // 1 2 1
const [a, b, ...[c, d]] = [1, 2, 3, 4];
console.log(a, b, c, d); // 1 2 3 4
这些绑定模式甚至可以嵌套,只要每个剩余属性是列表中的最后一个。
const [a, b, ...[c, d, ...[e, f]]] = [1, 2, 3, 4, 5, 6];
console.log(a, b, c, d, e, f); // 1 2 3 4 5 6
另一方面,对象解构只能将标识符作为剩余属性。
const { a, ...{ b } } = { a: 1, b: 2 };
// SyntaxError: `...` must be followed by an identifier in declaration contexts
let a, b;
({ a, ...{ b } } = { a: 1, b: 2 });
// SyntaxError: `...` must be followed by an assignable reference in assignment contexts
从正则表达式匹配中解包值
当正则表达式exec()
方法找到匹配项时,它会返回一个数组,其中首先包含字符串的整个匹配部分,然后是与正则表达式中每个带括号的组匹配的字符串部分。解构允许你轻松地从这个数组中解包这些部分,如果不需要完整匹配,则忽略它。
function parseProtocol(url) {
const parsedURL = /^(\w+):\/\/([^/]+)\/(.*)$/.exec(url);
if (!parsedURL) {
return false;
}
console.log(parsedURL);
// ["https://mdn.org.cn/en-US/docs/Web/JavaScript",
// "https", "developer.mozilla.org", "en-US/docs/Web/JavaScript"]
const [, protocol, fullHost, fullPath] = parsedURL;
return protocol;
}
console.log(
parseProtocol("https://mdn.org.cn/en-US/docs/Web/JavaScript"),
);
// "https"
对任何可迭代对象使用数组解构
数组解构调用右侧的可迭代协议。因此,任何可迭代对象,不一定是数组,都可以被解构。
const [a, b] = new Map([
[1, 2],
[3, 4],
]);
console.log(a, b); // [1, 2] [3, 4]
不可迭代对象不能作为数组解构。
const obj = { 0: "a", 1: "b", length: 2 };
const [a, b] = obj;
// TypeError: obj is not iterable
可迭代对象只会被迭代直到所有绑定都已赋值。
const obj = {
*[Symbol.iterator]() {
for (const v of [0, 1, 2, 3]) {
console.log(v);
yield v;
}
},
};
const [a, b] = obj; // Only logs 0 and 1
剩余绑定被急切地评估并创建一个新数组,而不是使用旧的可迭代对象。
const obj = {
*[Symbol.iterator]() {
for (const v of [0, 1, 2, 3]) {
console.log(v);
yield v;
}
},
};
const [a, b, ...rest] = obj; // Logs 0 1 2 3
console.log(rest); // [2, 3] (an array)
对象解构
基本赋值
const user = {
id: 42,
isVerified: true,
};
const { id, isVerified } = user;
console.log(id); // 42
console.log(isVerified); // true
赋值给新的变量名
可以从对象中解包属性并将其赋值给与对象属性名称不同的变量。
const o = { p: 42, q: true };
const { p: foo, q: bar } = o;
console.log(foo); // 42
console.log(bar); // true
例如,这里const { p: foo } = o
从对象o
中取出名为p
的属性并将其赋值给名为foo
的局部变量。
赋值给新变量名并提供默认值
一个属性可以同时:
- 从对象中解包并赋值给具有不同名称的变量。
- 在解包的值为
undefined
的情况下赋值一个默认值。
const { a: aa = 10, b: bb = 5 } = { a: 3 };
console.log(aa); // 3
console.log(bb); // 5
从作为函数参数传递的对象中解包属性
传递给函数参数的对象也可以解包到变量中,然后可以在函数体中访问这些变量。对于对象赋值,解构语法允许新变量与原始属性具有相同或不同的名称,并为原始对象未定义该属性的情况分配默认值。
考虑这个包含用户信息的对象。
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "Jane",
lastName: "Doe",
},
};
这里我们展示了如何将传递对象的属性解包到同名变量中。参数值{ id }
表示传递给函数的对象的id
属性应解包到同名变量中,然后可以在函数中使用该变量。
function userId({ id }) {
return id;
}
console.log(userId(user)); // 42
你可以定义解包变量的名称。这里我们解包名为displayName
的属性,并将其重命名为dname
以便在函数体中使用。
function userDisplayName({ displayName: dname }) {
return dname;
}
console.log(userDisplayName(user)); // "jdoe"
嵌套对象也可以解包。下面的示例显示了属性fullname.firstName
被解包到名为name
的变量中。
function whois({ displayName, fullName: { firstName: name } }) {
return `${displayName} is ${name}`;
}
console.log(whois(user)); // "jdoe is Jane"
设置函数参数的默认值
可以使用=
指定默认值,如果传递的对象中不存在指定的属性,这些默认值将用作变量值。
下面我们展示一个函数,其中默认大小为'big'
,默认坐标为x: 0, y: 0
,默认半径为 25。
function drawChart({
size = "big",
coords = { x: 0, y: 0 },
radius = 25,
} = {}) {
console.log(size, coords, radius);
// do some chart drawing
}
drawChart({
coords: { x: 18, y: 30 },
radius: 30,
});
在上面的drawChart
函数签名中,解构的左侧有一个空对象= {}
的默认值。
你也可以不带这个默认值来编写函数。但是,如果你省略这个默认值,函数在调用时将需要至少提供一个参数,而在其当前形式下,你可以调用drawChart()
而不提供任何参数。否则,你需要至少提供一个空对象字面量。
有关更多信息,请参阅默认参数 > 带有默认值赋值的解构参数。
嵌套对象和数组解构
const metadata = {
title: "Scratchpad",
translations: [
{
locale: "de",
localizationTags: [],
lastEdit: "2014-04-14T08:43:37",
url: "/en-US/docs/Tools/Scratchpad",
title: "JavaScript-Umgebung",
},
],
url: "/en-US/docs/Tools/Scratchpad",
};
const {
title: englishTitle, // rename
translations: [
{
title: localeTitle, // rename
},
],
} = metadata;
console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"
for...of 迭代和解构
const people = [
{
name: "Mike Smith",
family: {
mother: "Jane Smith",
father: "Harry Smith",
sister: "Samantha Smith",
},
age: 35,
},
{
name: "Tom Jones",
family: {
mother: "Norah Jones",
father: "Richard Jones",
brother: "Howard Jones",
},
age: 25,
},
];
for (const {
name: n,
family: { father: f },
} of people) {
console.log(`Name: ${n}, Father: ${f}`);
}
// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
计算对象属性名和解构
计算属性名,就像在对象字面量中一样,可以与解构一起使用。
const key = "z";
const { [key]: foo } = { z: "bar" };
console.log(foo); // "bar"
无效的 JavaScript 标识符作为属性名
解构可以与不是有效 JavaScript 标识符的属性名一起使用,方法是提供一个有效的替代标识符。
const foo = { "fizz-buzz": true };
const { "fizz-buzz": fizzBuzz } = foo;
console.log(fizzBuzz); // true
解构原始值
对象解构几乎等同于属性访问。这意味着如果你尝试解构原始值,该值将被包装到相应的包装对象中,并在包装对象上访问该属性。
const { a, toFixed } = 1;
console.log(a, toFixed); // undefined ƒ toFixed() { [native code] }
与访问属性一样,解构null
或undefined
会抛出TypeError
。
const { a } = undefined; // TypeError: Cannot destructure property 'a' of 'undefined' as it is undefined.
const { b } = null; // TypeError: Cannot destructure property 'b' of 'null' as it is null.
即使模式为空,也会发生这种情况。
const {} = null; // TypeError: Cannot destructure 'null' as it is null.
数组和对象解构的组合
数组和对象解构可以组合使用。假设你想要下面的数组props
中的第三个元素,然后你想要对象中的name
属性,你可以这样做:
const props = [
{ id: 1, name: "Fizz" },
{ id: 2, name: "Buzz" },
{ id: 3, name: "FizzBuzz" },
];
const [, , { name }] = props;
console.log(name); // "FizzBuzz"
解构对象时会查找原型链
解构对象时,如果属性本身未被访问,它将继续沿原型链查找。
const obj = {
self: "123",
__proto__: {
prot: "456",
},
};
const { self, prot } = obj;
console.log(self); // "123"
console.log(prot); // "456"
规范
规范 |
---|
ECMAScript® 2026 语言规范 # sec-destructuring-assignment |
ECMAScript® 2026 语言规范 # sec-destructuring-binding-patterns |
浏览器兼容性
加载中…