一、學習網站推薦
解構 - JavaScript | MDN
界面如下,既有知識點,也有在線編譯器執行代碼。初學者可以看看
二、Javascript什么是解構
解構語法是一種 Javascript 語法。可以將數組中的值或對象的屬性取出,賦值給其他變量。它可以在接收數據的位置使用(例如賦值的左側或創建新標識符綁定的任何位置)。
代碼如下:
let a, b, rest;
[a, b] = [10, 20];console.log(a);
// 期望輸出:10console.log(b);
// 期望輸出:20[a, b, ...rest] = [10, 20, 30, 40, 50];console.log(rest);
// 期望輸出:Array [30, 40, 50]
運行結果如下:
1020Array [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); // brackets 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;
// 等同于:
// 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;
// 等同于:
// 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;
// 變量 `a` 和 `d` 被綁定
所有變量共享相同的聲明,因此,如果你希望某些變量可重新分配,而其他變量是只讀的,則可能需要解構兩次——一次使用?let
,一次使用?const
。
const obj = { a: 1, b: { c: 2 } };
const { a } = obj; // a 為常量
let {b: { c: d },
} = obj; // d 可被重新賦值
你也可以在其他許多為你綁定變量的語法中,使用綁定解構模式。這些包括:
- 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);
// 屬性 `a` 和 `b` 被賦值給了 `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;// 等同于:
// const numbers[0] = obj.a;
// const numbers[1] = obj.b;
// 無效代碼
你只能在賦值運算符的左側使用賦值模式。不能與復合賦值運算符如?+=
?或?*=
?一起使用。
默認值
每個解構屬性都可以有一個默認值。當屬性不存在或值為?undefined
?時,將使用默認值。如果屬性的值為?null
,則不使用默認值。
const [a = 1] = []; // a 是 1
const { b = 2 } = { b: undefined }; // b 是 2
const { c = 2 } = { c: null }; // c 是 null
默認值可以是任何表達式。僅在必要時對其進行求值。
const { b = console.log("hey") } = { b: 2 };
// 不會輸出任何東西,因為 `b` 的值已經被定義,所以不需要求默認值。
剩余屬性和剩余元素
你可以使用剩余屬性(...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
// 始終考慮將剩余運算符作為最后一個元素
示例
解構數組
基本變量賦值
const foo = ["one", "two", "three"];const [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"
解構比源更多的元素
在從賦值語句右側指定的長度為?N?的數組解構的數組中,如果賦值語句左側指定的變量數量大于?N,則只有前?N?個變量被賦值。其余變量的值將是未定義。
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
交換變量
可以在一個解構表達式中交換兩個變量的值。
沒有解構的情況下,交換兩個變量需要一個臨時變量(或者用低級語言中的異或交換技巧)。
let a = 1;
let b = 3;[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1const 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); // 3const [c] = f();
console.log(c); // 1
你也可以忽略全部返回值:
[, ,] = f();
使用綁定模式作為剩余屬性
數組解構的剩余屬性可以是另一個數組或對象綁定模式。這允許你同時提取數組的屬性和索引。
const [a, b, ...{ pop, push }] = [1, 2];
console.log(a, b); // 1 2
console.log(pop, push); // [Function pop] [Function push]
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 contextslet 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://developer.mozilla.org/zh-CN/docs/Web/JavaScript",// "https", "developer.mozilla.org", "zh-CN/docs/Web/JavaScript"]const [, protocol, fullhost, fullpath] = parsedURL;return protocol;
}console.log(parseProtocol("https://developer.mozilla.org/zh-CN/docs/Web/JavaScript"),
);
// "https"
在任何可迭代對象上使用數組解構
數組解構調用右側的迭代協議。因此,任何可迭代對象(不一定是數組)都可以解構。
jsCopy to Clipboard
const [a, b] = new Map([[1, 2],[3, 4],
]);
console.log(a, b); // [1, 2] [3, 4]
不可迭代對象不能解構為數組。
jsCopy to Clipboard
const obj = { 0: "a", 1: "b", length: 2 };
const [a, b] = obj;
// TypeError: obj is not iterable
只有在分配所有綁定之前,才會迭代可迭代對象。
jsCopy to Clipboard
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
其余的綁定會提前求值并創建一個新數組,而不是使用舊的迭代器。
jsCopy to Clipboard
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)
解構對象
基本賦值
jsCopy to Clipboard
const user = {id: 42,isVerified: true,
};const { id, isVerified } = user;console.log(id); // 42
console.log(isVerified); // true
賦值給新的變量名
可以從對象中提取屬性,并將其賦值給名稱與對象屬性不同的變量。
jsCopy to Clipboard
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
。
jsCopy to Clipboard
const { a: aa = 10, b: bb = 5 } = { a: 3 };console.log(aa); // 3
console.log(bb); // 5
從作為函數參數傳遞的對象中提取屬性
傳遞給函數參數的對象也可以提取到變量中,然后可以在函數體內訪問這些變量。至于對象賦值,解構語法允許新變量具有與原始屬性相同或不同的名稱,并為原始對象未定義屬性的情況分配默認值。
請考慮此對象,其中包含有關用戶的信息。
jsCopy to Clipboard
const user = {id: 42,displayName: "jdoe",fullName: {firstName: "Jane",lastName: "Doe",},
};
在這里,我們展示了如何將傳遞對象的屬性提取到具有相同名稱的變量。參數值?{ id }
?表示傳遞給函數的對象的?id
?屬性應該被提取到一個同名變量中,然后可以在函數中使用。
jsCopy to Clipboard
function userId({ id }) {return id;
}console.log(userId(user)); // 42
你可以定義提取變量的名稱。在這里,我們提取名為?displayName
?的屬性,并將其重命名為?dname
,以便在函數體內使用。
jsCopy to Clipboard
function userDisplayName({ displayName: dname }) {return dname;
}console.log(userDisplayName(user)); // `jdoe`
嵌套對象也可以提取。下面的示例展示了屬性?fullname.firstName
?被提取到名為?name
?的變量中。
jsCopy to Clipboard
function whois({ displayName, fullName: { firstName: name } }) {return `${displayName} is ${name}`;
}console.log(whois(user)); // "jdoe is Jane"
設置函數參數的默認值
默認值可以使用?=
?指定,如果指定的屬性在傳遞的對象中不存在,則將其用作變量值。
下面我們展示了一個默認大小為?big
的函數,默認坐標為?x: 0, y: 0
,默認半徑為 25。
jsCopy to Clipboard
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()
。否則,你至少需要提供一個空對象字面量。
有關詳細信息,請參閱默認參數值 > 有默認值的解構參數。
解構嵌套對象和數組
jsCopy to Clipboard
const metadata = {title: "Scratchpad",translations: [{locale: "de",localization_tags: [],last_edit: "2014-04-14T08:43:37",url: "/de/docs/Tools/Scratchpad",title: "JavaScript-Umgebung",},],url: "/zh-CN/docs/Tools/Scratchpad",
};let {title: englishTitle, // renametranslations: [{title: localeTitle, // rename},],
} = metadata;console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"
For of 迭代和解構
jsCopy to Clipboard
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。
jsCopy to Clipboard
const { a } = undefined; // TypeError: Cannot destructure property 'a' of 'undefined' as it is undefined.
const { a } = null; // TypeError: Cannot destructure property 'b' of 'null' as it is null.
即使模式為空,也會發生這種情況。
const {} = null; // TypeError: Cannot destructure 'null' as it is null.
組合數組和對象解構
數組和對象解構可以組合使用。假設你想要下面?props
?數組中的第三個元素,然后你想要對象中的?name
?屬性,你可以執行以下操作:
jsCopy to Clipboard
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"
規范
Specification |
---|
ECMAScript? 2026 Language?Specification#?sec-destructuring-assignment |
ECMAScript? 2026 Language?Specification#?sec-destructuring-binding-patterns |