解构赋值

解构赋值语法是一种 JavaScript 表达式,可以将数组中的值或对象中的属性解压到不同的变量中。

¥The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

Try it

语法

¥Syntax

js
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);

描述

¥Description

对象和数组文字表达式提供了一种创建临时数据包的简单方法。

¥The object and array literal expressions provide an easy way to create ad hoc packages of data.

js
const x = [1, 2, 3, 4, 5];

解构赋值使用类似的语法,但在赋值的左侧使用它。它定义从源变量中解包哪些值。

¥The destructuring assignment uses similar syntax but uses it on the left-hand side of the assignment instead. It defines which values to unpack from the sourced variable.

js
const x = [1, 2, 3, 4, 5];
const [y, z] = x;
console.log(y); // 1
console.log(z); // 2

同样,你可以解构赋值左侧的对象。

¥Similarly, you can destructure objects on the left-hand side of the assignment.

js
const obj = { a: 1, b: 2 };
const { a, b } = obj;
// is equivalent to:
// const a = obj.a;
// const b = obj.b;

此功能类似于 Perl 和 Python 等语言中的功能。

¥This capability is similar to features present in languages such as Perl and Python.

有关数组或对象解构的特定功能,请参阅下面的各个 examples

¥For features specific to array or object destructuring, refer to the individual examples below.

绑定和赋值

¥Binding and assignment

对于对象和数组解构,有两种解构模式:binding 模式和赋值模式,语法略有不同。

¥For both object and array destructuring, there are two kinds of destructuring patterns: binding pattern and assignment pattern, with slightly different syntaxes.

在绑定模式中,模式以声明关键字(varletconst)开头。然后,每个单独的属性必须绑定到一个变量或进一步解构。

¥In binding patterns, the pattern starts with a declaration keyword (var, let, or const). Then, each individual property must either be bound to a variable or further destructured.

js
const obj = { a: 1, b: { c: 2 } };
const {
  a,
  b: { c: d },
} = obj;
// Two variables are bound: `a` and `d`

所有变量共享相同的声明,因此如果你希望某些变量可重新分配而其他变量是只读的,你可能需要解构两次 - 一次使用 let,一次使用 const

¥All variables share the same declaration, so if you want some variables to be re-assignable but others to be read-only, you may have to destructure twice — once with let, once with const.

js
const obj = { a: 1, b: { c: 2 } };
const { a } = obj; // a is constant
let {
  b: { c: d },
} = obj; // d is re-assignable

在语言为你绑定变量的许多其他语法中,你可以使用绑定解构模式。这些包括:

¥In many other syntaxes where the language binds a variable for you, you can use a binding destructuring pattern. These include:

在赋值模式中,模式不以关键字开头。每个解构属性都被分配给一个赋值目标 - 可以预先用 varlet 声明,或者是另一个对象的属性 - 一般来说,可以出现在赋值表达式左侧的任何内容。

¥In assignment patterns, the pattern does not start with a keyword. Each destructured property is assigned to a target of assignment — which may either be declared beforehand with var or let, or is a property of another object — in general, anything that can appear on the left-hand side of an assignment expression.

js
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`

注意:当使用没有声明的对象字面量解构赋值时,赋值语句周围需要括号 ( ... )

¥Note: The parentheses ( ... ) around the assignment statement are required when using object literal destructuring assignment without a declaration.

{ a, b } = { a: 1, b: 2 } 不是有效的独立语法,因为根据 表达式语句 的规则,左侧的 { a, b } 被视为块而不是对象文字。但是,({ a, b } = { a: 1, b: 2 }) 有效,const { a, b } = { a: 1, b: 2 } 也有效。

¥{ a, b } = { a: 1, b: 2 } is not valid stand-alone syntax, as the { a, b } on the left-hand side is considered a block and not an object literal according to the rules of expression statements. However, ({ a, b } = { a: 1, b: 2 }) is valid, as is const { a, b } = { a: 1, b: 2 }.

如果你的编码风格不包含尾随分号,则 ( ... ) 表达式前面需要有一个分号,否则它可能用于执行上一行的函数。

¥If your coding style does not include trailing semicolons, the ( ... ) expression needs to be preceded by a semicolon, or it may be used to execute a function on the previous line.

请注意,上述代码的等效绑定模式不是有效语法:

¥Note that the equivalent binding pattern of the code above is not valid syntax:

js
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.

你只能使用赋值模式作为 assignment 运算符的左侧。你不能将它们与复合赋值运算符(例如 +=*=)一起使用。

¥You can only use assignment patterns as the left-hand side of the assignment operator. You cannot use them with compound assignment operators such as += or *=.

默认值

¥Default value

每个解构属性都可以有一个默认值。当该属性不存在或值为 undefined 时,将使用默认值。如果该属性的值为 null,则不会使用它。

¥Each destructured property can have a default value. The default value is used when the property is not present, or has value undefined. It is not used if the property has value null.

js
const [a = 1] = []; // a is 1
const { b = 2 } = { b: undefined }; // b is 2
const { c = 2 } = { c: null }; // c is null

默认值可以是任何表达式。仅在必要时才会对其进行评估。

¥The default value can be any expression. It will only be evaluated when necessary.

js
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 property

你可以使用剩余属性 ...rest 结束解构模式。此模式会将对象或数组的所有剩余属性存储到新对象或数组中。

¥You can end a destructuring pattern with a rest property ...rest. This pattern will store all remaining properties of the object or array into a new object or array.

js
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]

其余属性必须是模式中的最后一个,并且不能有尾随逗号。

¥The rest property must be the last in the pattern, and must not have a trailing comma.

js
const [a, ...b,] = [1, 2, 3];

// SyntaxError: rest element may not have a trailing comma
// Always consider using rest operator as the last element

示例

¥Examples

数组解构

¥Array destructuring

基本变量赋值

¥Basic variable assignment

js
const foo = ["one", "two", "three"];

const [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"

使用比源更多的元素进行解构

¥Destructuring with more elements than the source

在从赋值右侧指定的长度为 N 的数组进行解构的数组中,如果赋值左侧指定的变量数量大于 N,则仅对前 N 个变量赋值。其余变量的值将是未定义的。

¥In an array destructuring from an array of length N specified on the right-hand side of the assignment, if the number of variables specified on the left-hand side of the assignment is greater than N, only the first N variables are assigned values. The values of the remaining variables will be undefined.

js
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

交换变量

¥Swapping variables

两个变量值可以在一个解构表达式中交换。

¥Two variables values can be swapped in one destructuring expression.

如果没有解构赋值,交换两个值需要一个临时变量(或者,在某些底层语言中,需要 异或交换技巧)。

¥Without destructuring assignment, swapping two values requires a temporary variable (or, in some low-level languages, the XOR-swap trick).

js
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]

解析从函数返回的数组

¥Parsing an array returned from a function

从函数返回数组始终是可能的。解构可以使数组返回值的处理更加简洁。

¥It's always been possible to return an array from a function. Destructuring can make working with an array return value more concise.

在此示例中,f() 返回值 [1, 2] 作为其输出,可以通过解构在单行中解析该值。

¥In this example, f() returns the values [1, 2] as its output, which can be parsed in a single line with destructuring.

js
function f() {
  return [1, 2];
}

const [a, b] = f();
console.log(a); // 1
console.log(b); // 2

忽略一些返回值

¥Ignoring some returned values

你可以忽略你不感兴趣的返回值:

¥You can ignore return values that you're not interested in:

js
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

你还可以忽略所有返回值:

¥You can also ignore all returned values:

js
[, ,] = f();

使用绑定模式作为剩余属性

¥Using a binding pattern as the rest property

数组解构赋值的剩余属性可以是另一个数组或对象绑定模式。内部解构对收集其余元素后创建的数组进行解构,因此你无法以这种方式访问原始可迭代对象上存在的任何属性。

¥The rest property of array destructuring assignment can be another array or object binding pattern. The inner destructuring destructures from the array created after collecting the rest elements, so you cannot access any properties present on the original iterable in this way.

js
const [a, b, ...{ length }] = [1, 2, 3];
console.log(a, b, length); // 1 2 1
js
const [a, b, ...[c, d]] = [1, 2, 3, 4];
console.log(a, b, c, d); // 1 2 3 4

这些绑定模式甚至可以嵌套,只要每个剩余属性是列表中的最后一个即可。

¥These binding patterns can even be nested, as long as each rest property is the last in the list.

js
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

另一方面,对象解构只能有一个标识符作为剩余属性。

¥On the other hand, object destructuring can only have an identifier as the rest property.

js
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

从正则表达式匹配中解压值

¥Unpacking values from a regular expression match

当正则表达式 exec() 方法找到匹配项时,它返回一个数组,该数组首先包含字符串的整个匹配部分,然后包含与正则表达式中每个括号组匹配的字符串部分。解构赋值允许你轻松地从该数组中解压缩各个部分,如果不需要则忽略完整匹配。

¥When the regular expression exec() method finds a match, it returns an array containing first the entire matched portion of the string and then the portions of the string that matched each parenthesized group in the regular expression. Destructuring assignment allows you to unpack the parts out of this array easily, ignoring the full match if it is not needed.

js
function parseProtocol(url) {
  const parsedURL = /^(\w+):\/\/([^/]+)\/(.*)$/.exec(url);
  if (!parsedURL) {
    return false;
  }
  console.log(parsedURL);
  // ["https://web.nodejs.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://web.nodejs.cn/en-US/docs/Web/JavaScript"),
);
// "https"

对任何可迭代对象使用数组解构

¥Using array destructuring on any iterable

数组解构调用右侧的 可迭代协议。因此,任何可迭代的(不一定是数组)都可以被解构。

¥Array destructuring calls the iterable protocol of the right-hand side. Therefore, any iterable, not necessarily arrays, can be destructured.

js
const [a, b] = new Map([
  [1, 2],
  [3, 4],
]);
console.log(a, b); // [1, 2] [3, 4]

不可迭代对象不能被解构为数组。

¥Non-iterables cannot be destructured as arrays.

js
const obj = { 0: "a", 1: "b", length: 2 };
const [a, b] = obj;
// TypeError: obj is not iterable

仅迭代可迭代对象,直到分配了所有绑定。

¥Iterables are only iterated until all bindings are assigned.

js
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

其余绑定会被预求值并创建一个新数组,而不是使用旧的可迭代对象。

¥The rest binding is eagerly evaluated and creates a new array, instead of using the old iterable.

js
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)

对象解构

¥Object destructuring

基本作业

¥Basic assignment

js
const user = {
  id: 42,
  isVerified: true,
};

const { id, isVerified } = user;

console.log(id); // 42
console.log(isVerified); // true

分配给新变量名称

¥Assigning to new variable names

属性可以从对象中解包并分配给与对象属性名称不同的变量。

¥A property can be unpacked from an object and assigned to a variable with a different name than the object property.

js
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 的局部变量。

¥Here, for example, const { p: foo } = o takes from the object o the property named p and assigns it to a local variable named foo.

分配新的变量名称并提供默认值

¥Assigning to new variable names and providing default values

一个属性可以同时是

¥A property can be both

  • 从对象解压并分配给具有不同名称的变量。
  • 如果解压后的值为 undefined,则分配一个默认值。
js
const { a: aa = 10, b: bb = 5 } = { a: 3 };

console.log(aa); // 3
console.log(bb); // 5

从作为函数参数传递的对象中解压属性

¥Unpacking properties from objects passed as a function parameter

传递到函数参数的对象也可以解压缩为变量,然后可以在函数体内访问这些变量。至于对象分配,解构语法允许新变量与原始属性具有相同的名称或不同的名称,并在原始对象未定义属性的情况下分配默认值。

¥Objects passed into function parameters can also be unpacked into variables, which may then be accessed within the function body. As for object assignment, the destructuring syntax allows for the new variable to have the same name or a different name than the original property, and to assign default values for the case when the original object does not define the property.

考虑这个对象,它包含有关用户的信息。

¥Consider this object, which contains information about a user.

js
const user = {
  id: 42,
  displayName: "jdoe",
  fullName: {
    firstName: "Jane",
    lastName: "Doe",
  },
};

这里我们展示如何将传递的对象的属性解包到具有相同名称的变量中。参数值 { id } 表示传递给函数的对象的 id 属性应解包到同名变量中,然后可以在函数内使用。

¥Here we show how to unpack a property of the passed object into a variable with the same name. The parameter value { id } indicates that the id property of the object passed to the function should be unpacked into a variable with the same name, which can then be used within the function.

js
function userId({ id }) {
  return id;
}

console.log(userId(user)); // 42

你可以定义解压变量的名称。这里我们解压名为 displayName 的属性,并将其重命名为 dname 以在函数体内使用。

¥You can define the name of the unpacked variable. Here we unpack the property named displayName, and rename it to dname for use within the function body.

js
function userDisplayName({ displayName: dname }) {
  return dname;
}

console.log(userDisplayName(user)); // "jdoe"

嵌套对象也可以被解包。下面的示例显示属性 fullname.firstName 被解包到名为 name 的变量中。

¥Nested objects can also be unpacked. The example below shows the property fullname.firstName being unpacked into a variable called name.

js
function whois({ displayName, fullName: { firstName: name } }) {
  return `${displayName} is ${name}`;
}

console.log(whois(user)); // "jdoe is Jane"

设置函数参数的默认值

¥Setting a function parameter's default value

可以使用 = 指定默认值,如果传递的对象中不存在指定的属性,则默认值将用作变量值。

¥Default values can be specified using =, and will be used as variable values if a specified property does not exist in the passed object.

下面我们展示一个默认大小为 'big'、默认坐标为 x: 0, y: 0、默认半径为 25 的函数。

¥Below we show a function where the default size is 'big', default co-ordinates are x: 0, y: 0 and default radius is 25.

js
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 的函数签名中,解构后的左侧具有空对象 = {} 的默认值。

¥In the function signature for drawChart above, the destructured left-hand side has a default value of an empty object = {}.

你也可以编写没有默认值的函数。但是,如果你省略该默认值,该函数将在调用时查找至少一个要提供的参数,而在其当前形式中,你可以在不提供任何参数的情况下调用 drawChart()。否则,你至少需要提供一个空对象文字。

¥You could have also written the function without that default. However, if you leave out that default value, the function will look for at least one argument to be supplied when invoked, whereas in its current form, you can call drawChart() without supplying any parameters. Otherwise, you need to at least supply an empty object literal.

欲了解更多信息,请参阅 默认参数 > 具有默认值分配的解构参数

¥For more information, see Default parameters > Destructured parameter with default value assignment.

嵌套对象和数组解构

¥Nested object and array destructuring

js
const metadata = {
  title: "Scratchpad",
  translations: [
    {
      locale: "de",
      localizationTags: [],
      lastEdit: "2014-04-14T08:43:37",
      url: "/de/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 iteration and destructuring

js
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"

计算对象属性名称和解构

¥Computed object property names and destructuring

计算属性名称(如 对象字面量 上的属性名称)可以与解构一起使用。

¥Computed property names, like on object literals, can be used with destructuring.

js
const key = "z";
const { [key]: foo } = { z: "bar" };

console.log(foo); // "bar"

作为属性名称的 JavaScript 标识符无效

¥Invalid JavaScript identifier as a property name

通过提供有效的替代标识符,解构可以与非有效 JavaScript identifiers 的属性名称一起使用。

¥Destructuring can be used with property names that are not valid JavaScript identifiers by providing an alternative identifier that is valid.

js
const foo = { "fizz-buzz": true };
const { "fizz-buzz": fizzBuzz } = foo;

console.log(fizzBuzz); // true

解构原始值

¥Destructuring primitive values

对象解构几乎等同于 属性访问。这意味着如果你尝试破坏原始值,该值将被封装到相应的封装对象中,并且在封装对象上访问该属性。

¥Object destructuring is almost equivalent to property accessing. This means if you try to destruct a primitive value, the value will get wrapped into the corresponding wrapper object and the property is accessed on the wrapper object.

js
const { a, toFixed } = 1;
console.log(a, toFixed); // undefined ƒ toFixed() { [native code] }

与访问属性相同,解构 nullundefined 会抛出 TypeError

¥Same as accessing properties, destructuring null or undefined throws a TypeError.

js
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.

即使模式为空,也会发生这种情况。

¥This happens even when the pattern is empty.

js
const {} = null; // TypeError: Cannot destructure 'null' as it is null.

组合数组和对象解构

¥Combined array and object destructuring

数组和对象解构可以结合起来。假设你想要下面数组 props 中的第三个元素,然后你想要对象中的 name 属性,你可以执行以下操作:

¥Array and object destructuring can be combined. Say you want the third element in the array props below, and then you want the name property in the object, you can do the following:

js
const props = [
  { id: 1, name: "Fizz" },
  { id: 2, name: "Buzz" },
  { id: 3, name: "FizzBuzz" },
];

const [, , { name }] = props;

console.log(name); // "FizzBuzz"

对象解构时会查找原型链

¥The prototype chain is looked up when the object is deconstructed

解构一个对象时,如果某个属性本身没有被访问,就会沿着原型链继续查找。

¥When deconstructing an object, if a property is not accessed in itself, it will continue to look up along the prototype chain.

js
const obj = {
  self: "123",
  __proto__: {
    prot: "456",
  },
};
const { self, prot } = obj;

console.log(self); // "123"
console.log(prot); // "456"

规范

Specification
ECMAScript Language Specification
# sec-destructuring-assignment
ECMAScript Language Specification
# sec-destructuring-binding-patterns

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看

¥See also