--- title: 解構賦值 slug: Web/JavaScript/Reference/Operators/Destructuring_assignment tags: - Destructuring - Destructuring_assignment - ECMAScript 2015 - ES6 - JavaScript - Language feature - Operator - 解構 translation_of: Web/JavaScript/Reference/Operators/Destructuring_assignment ---
解構賦值 (Destructuring assignment) 語法是一種 JavaScript 運算式,可以把陣列或物件中的資料解開擷取成為獨立變數。
let a, b, rest; [a, b] = [10, 20]; console.log(a); // 10 console.log(b); // 20 [a, b, ...rest] = [10, 20, 30, 40, 50]; console.log(a); // 10 console.log(b); // 20 console.log(rest); // [30, 40, 50] ({ a, b } = { a: 10, b: 20 }); console.log(a); // 10 console.log(b); // 20 // Stage 4(finished) proposal ({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}); console.log(a); // 10 console.log(b); // 20 console.log(rest); // {c: 30, d: 40}
物件與陣列運算式提供了簡單的方式,建立特定的資料組。
const x = [1, 2, 3, 4, 5];
解構賦值使用類似語法;不過在指定敘述式的左側,要宣告從來源變數接收解開值之變數。
const x = [1, 2, 3, 4, 5]; const [y, z] = x; console.log(y); // 1 console.log(z); // 2
Perl 和 Python 也有類似的語法和功能。
const foo = ['one', 'two', 'three']; const [red, yellow, green] = foo; console.log(red); // "one" console.log(yellow); // "two" console.log(green); // "three"
變數可以在宣告式後,再透過解構賦值。
let a, b; [a, b] = [1, 2]; console.log(a); // 1 console.log(b); // 2
當解構來源陣列對應的元素是 undefined 時,變數可以被設定預設值。
let a, b; [a=5, b=7] = [1]; console.log(a); // 1 console.log(b); // 7
兩個變數可以透過一個解構指派式交換。
沒有解構指派式時,這需要一個暫存變數來達成(或者像某些低階語言的 XOR-swap trick)。
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]; } let a, b; [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
當然你也可以忽略全部回傳值:
[,,] = f();
解構一個陣列時,你可以透過其餘元素(rest pattern)將來源剩下之元素指派到一個變數:
const [a, ...b] = [1, 2, 3]; console.log(a); // 1 console.log(b); // [2, 3]
要注意的是,當左邊函式裡使用其餘解構,同時使用結尾逗號,這樣會拋出例外 {{jsxref("SyntaxError")}} :
const [a, ...b,] = [1, 2, 3]; // SyntaxError 語法錯誤: 其餘元素不可以跟隨結尾逗號 // 需要把其餘運算子放在最後的元素
當正則運算式的方法 exec()
比對到一個值,其回傳陣列中的第一個值是相符的完整字串,後績的則是比對到正則運算式每組括號內的部分。當你沒需要利用第一個完整比對結果時,解構指派式讓你更簡單的取出後績元素。
function parseProtocol(url) { const parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url); if (!parsedURL) { return false; } console.log(parsedURL); // ["https://developer.mozilla.org/en-US/Web/JavaScript", "https", "developer.mozilla.org", "en-US/Web/JavaScript"] const [, protocol, fullhost, fullpath] = parsedURL; return protocol; } console.log(parseProtocol('https://developer.mozilla.org/en-US/Web/JavaScript')); // "https"
const o = {p: 42, q: true}; const {p, q} = o; console.log(p); // 42 console.log(q); // true
變數可以在宣告式後,再透過解構進行指派。
let a, b; ({a, b} = {a:1, b:2});
注意:當針對物件進行解構,而該句式沒有進行宣告時,指派式外必須加上括號 ( ... )
。
{a, b} = {a: 1, b: 2}
不是有效的獨立語法,因為左邊的 {a, b}
被視為程式碼區塊而非物件。
然而,({a, b} = {a: 1, b: 2})
是有效的,如同 const {a, b} = {a: 1, b: 2}
( ... )
表達式前句需要以分號結束,否則可能把上一句視為函式隨即執行。
物件中的屬性可以解構並擷取到名稱跟該屬性不一樣的變數。
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 = 10, b = 5} = {a: 3}; console.log(a); // 3 console.log(b); // 5
屬性 1) 可以從物件中被解開,且被指定一個不同名稱的變數及 2) 同時指定一個預設值,在解開的值為 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: 'John', lastName: 'Doe' } }; function userId({id}) { return id; } function whois({displayName, fullName: {firstName: name}}) { return `${displayName} is ${name}`; } console.log(userId(user)); // 42 console.log(whois(user)); // "jdoe is John"
這樣從 user 物件中提出了 id
, displayName
和 firstName
並且印出。
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
中,左方之解構式被指派到一個空物件: {size = 'big', coords = {x: 0, y: 0}, radius = 25} = {}
。你也可以略過填寫右方的指派式。不過,當你沒有使用右方指派式時,函式在呼叫時會找出最少一個參數。透過上述形式,你可以直接不使用參數的呼叫 drawChart()
。當你希望在呼叫這個函式時不傳送參數,這個設計會帶來方便。而另一個設計則能讓你確保函式必須傳上一個物件作為參數。
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: '/en-US/docs/Tools/Scratchpad' }; let { title: englishTitle, // rename translations: [ { title: localeTitle, // rename }, ], } = metadata; console.log(englishTitle); // "Scratchpad" console.log(localeTitle); // "JavaScript-Umgebung"
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"
物件演算屬性名稱(像是在 object literals)可以在解構指派式使用。
let key = 'z'; let {[key]: foo} = {z: 'bar'}; console.log(foo); // "bar"
ECMAScript 中的其餘/展開屬性在 proposal (stage 4) 新增了在解構式內使用其餘 (rest) 語法的定義。其餘屬性可以收集解構式中沒有指定的屬性值。
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40} a; // 10 b; // 20 rest; // { c: 30, d: 40 }
解構賦值可以透過另一個符合 JavaScript 識別字的變數名稱來解出不符合識別字的屬性。
const foo = { 'fizz-buzz': true }; const { 'fizz-buzz': fizzBuzz } = foo; console.log(fizzBuzz); // "true"
矩陣及物件解構可以混合進行。與例來說,你只需要使用下列 props
矩陣中第三個元素之物件中的 name
屬性,你可以如下面的例子進行解構:
const props = [ { id: 1, name: 'Fizz'}, { id: 2, name: 'Buzz'}, { id: 3, name: 'FizzBuzz'} ]; const [,, { name }] = props; console.log(name); // "FizzBuzz"
在進行物件解構時,如果一個屬性不在其當下存取,將會透過原型鏈 (prototype chain) 來進行追溯。
let obj = {self: '123'}; obj.__proto__.prot = '456'; const {self, prot} = obj; // self "123" // prot "456"(Access to the prototype chain)
規範 |
---|
{{SpecName('ESDraft', '#sec-destructuring-assignment', 'Destructuring assignment')}} |
{{Compat("javascript.operators.destructuring")}}