# Can you give an example for destructuring an object or an array? > 舉例 Array 或 Object 的解構賦值 > 解構賦值 (Destructuring assignment) 語法是 JavaScript ES6 登場的運算式,可以把陣列或物件中的資料解開擷取成為獨立變數。 ## 回答範例 :::info :bookmark: **Array destructuring** ``` javascript= // Variable assignment. const foo = ['one', 'two', 'three']; const [one, two, three] = foo; console.log(one); // "one" console.log(two); // "two" console.log(three); // "three" // Swapping variables let a = 1; let b = 3; [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1 ``` :bookmark: **Object destructuring** ```javascript= // Variable assignment. const o = {p: 42, q: true}; const {p, q} = o; console.log(p); // 42 console.log(q); // true ``` ::: --- ## 陣列解構賦值(Array Destructuring) 以前給陣列元素賦值的寫法 ``` javascript= let numbers = [1, 2, 3]; let a = numbers[0]; let b = numbers[1]; let c = numbers[2]; console.log(a,b,c); // 1, 2, 3 ``` :arrow_down:**ES6 開始可以直接透過解構的方式賦值**:arrow_down: ### 基本變數指定敘述 (Basic variable assignment) ``` javascript= // 指定敘述式的左側,要宣告從來源變數接收解開值的變數。 const foo = ['one', 'two', 'three']; const [red, yellow, green] = foo; console.log(red); // "one" console.log(yellow); // "two" console.log(green); // "three" ``` ### 宣告指派分開敍述 (Assignment separate from declaration) ``` javascript= // 變數可以在宣告式後,再透過解構賦值。 let a, b; [a, b] = [1, 2]; console.log(a); // 1 console.log(b); // 2 ``` ### 預設值 (Default values) ``` javascript= // 當解構來源陣列對應的元素是 undefined 時,變數可以被設定預設值。 let a, b; [a=5, b=7] = [1]; console.log(a); // 1 console.log(b); // 7 ``` ``` javascript= // 陣列解構中賦予預設值 let [a, b, c = 4, d = 'Hello'] = [1, 2, 3]; console.log(a, b, c, d); // 1, 2, 3, "Hello" ``` ### 變數交換 (Swapping variables) ```javascript= // 兩個變數可以透過一個解構指派式交換。 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] ``` ### 解析自函式回傳的陣列 ``` javascript= // 一直以來函式都可以回傳陣列,而解構指派式可以讓回傳的值更加簡潔。 function f() { return [1, 2]; } const [a, b] = f(); console.log(a); // 1 console.log(b); // 2 ``` ``` javascript= // 可以忽略某些回傳值 function f() { return [1, 2, 3]; } const [a, , b] = f(); console.log(a); // 1 console.log(b); // 3 ``` ### 把陣列剩餘部分解構到一個變數 ```javascript= // 透過其餘元素(rest pattern)將來源剩下之元素指派到一個變數: const [a, ...b] = [1, 2, 3]; console.log(a); // 1 console.log(b); // [2, 3] ``` --- ## 物件解構賦值(Object Destructuring) **陣列**的解構賦值強調**順序**,而**物件**的解構賦值強調**屬性名稱**, 屬性名稱必須相互對應才能夠取得到值。 ### 基本指派式 (Basic assignment) ```javascript= const o = {p: 42, q: true}; const {p, q} = o; console.log(p); // 42 console.log(q); // true ``` ```javascript= const obj = { website: "pjchender", country: "Taiwan" } let {website, country} = obj; console.log(website); // pjchender console.log(country); // Taiwan ``` :::success 程式碼中 const {website, country} = obj ,實際上完整的寫法應該是像這樣子(也就是說上面那段程式碼是簡寫): ``` let {website:website, country:country} = obj; ``` 它會根據前面的屬性名稱來對應要給的值,但值其實是給冒號(:)後面的變數,用圖來看像是這樣子: ![](https://i.imgur.com/NPprQMh.png) 所以真正被建立和賦值的是 let{ } 當中,冒號(:)後的變數。 我們可以透過另一個例子更容易了解這個概念: ``` javascript= let obj = { website: "pjchender", country: "Taiwan" } // 指派到新的變數名稱 let {website:wb, country:ct} = obj; console.log(website, country); // Error:website in not defined console.log(wb, ct) // "pjchender", "Taiwan" ``` 在物件解構賦值中,冒號前是用來對應物件的屬性名稱,冒號後才是真正建立的變數名稱和被賦值的對象。 ::: ### 無宣告指派 (Assignment without declaration) ``` javascript= // 變數可以在宣告式後,再透過解構進行指派。 let a, b; ({a, b} = {a:1, b:2}); ``` :::danger 當針對物件進行解構,而該句式沒有進行宣告時,指派式外必須加上小括號。 {a, b} = {a: 1, b: 2} 不是有效的獨立語法,因為左邊的 {a, b} 被視為程式碼區塊而非物件。 然而,({a, b} = {a: 1, b: 2}) 是有效的,如同 const {a, b} = {a: 1, b: 2}。 ( ... ) 表達式前句需要以分號結束,否則可能把上一句視為函式隨即執行。 ::: ### 預設值 ``` javascript= // 當解構物件中對應的值是 undefined 時,變數可以設定預設值。 const {a = 10, b = 5} = {a: 3}; console.log(a); // 3 console.log(b); // 5 ``` ### 指定新的變數名稱及預設值 ```javascript= const {a:aa = 10, b:bb = 5} = {a: 3}; console.log(aa); // 3 console.log(bb); // 5 ``` ### 從作為函式參數的物件中提出某屬性的值 ``` javascript= 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" ``` ### 物件解構時使用其餘變數 ``` javascript= let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40} a; // 10 b; // 20 rest; // { c: 30, d: 40 } ``` --- ## 混合使用陣列及物件解構 ``` javascript= const props = [ { id: 1, name: 'Fizz'}, { id: 2, name: 'Buzz'}, { id: 3, name: 'FizzBuzz'} ]; const [,, { name }] = props; console.log(name); // "FizzBuzz" ``` ``` javascript= // 混用物件與陣列 const {prop: x, prop2: [, y]} = {prop: 5, prop2: [10, 100]} console.log(x, y) // => 5 100 // 複雜多層次的物件 const { prop: x, prop2: { prop2: { nested: [ , , b] } } } = { prop: "Hello", prop2: { prop2: { nested: ["a", "b", "c"]}}} console.log(x, b) // => Hello c ``` ## 從非陣列或非物件(原始資料類型值)解構賦值 解構賦值的語法是針對物件或陣列的資料結構所設計。 從非陣列或非物件(Primitive Data Type)解構賦值要不就是產生錯誤,要不然就是賦不到值(得到undefined)。 ``` javascript= const [a] = undefined const {b} = null //TypeError: Invalid attempt to destructure non-iterable instance const {a} = false const {b} = 10 const {c} = 'hello' console.log(a, b, c) // undefined undefined undefined const [a] = false const [b] = 10 const [c] = 'hello' console.log( a, b, c) // TypeError: false is not iterable // TypeError: 10 is not iterable // 'h' (唯一的例外只有字串類型的值可以解構出單字元的字串值) ``` ## 解構賦值是深拷貝?淺拷貝? ``` javascript= const a = { name: 'name', age: 18, marriage: false, addr: { province: 'xinyi', city: 'taipei' } } let { name, age, marriage, addr } = a name = 'hihi' age = 30 marriage = true addr.province = 'taoyuan' addr.city = 'taoyuan' console.log(name, age, marriage, addr) console.log(a) // hihi 30 true {province: "taoyuan", city: "taoyuan"} // { name: "name", age: 18, marriage: false, addr: {province: "taoyuan", city: "taoyuan"} } ``` 從對象 a 中解構賦值了 name、age、marriage、addr 四個變數, 分別是 string、number、boolean 、object 類型, 改變這四個變數的值後,再與 a 原來的值作對比。 可以發現 a 的 name,age,marriage 屬性沒有改變,而 addr 屬性發生了改變。 如果解構的原對像是一維物件,其本質就是對 Primitive Data Type 進行等號賦值,那它就是深拷貝; 如果是多維物件,其本質就是對 Reference Data Type 進行等號賦值,那它就是淺拷貝; 最終的結論就是:解構賦值是淺拷貝(因為它確實不能對物件達到深拷貝的作用) --- [github 原題目](https://github.com/yangshun/front-end-interview-handbook/blob/master/contents/en/javascript-questions.md#can-you-give-an-example-for-destructuring-an-object-or-an-array) [MDN - 解構賦值](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) [JavaScript ES6 中的物件解構賦值](https://pjchender.blogspot.com/2017/01/es6-object-destructuring.html)