解構
解構語法是 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.club.tw/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.club.tw/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 |
瀏覽器相容性
載入中…