# 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;
```
它會根據前面的屬性名稱來對應要給的值,但值其實是給冒號(:)後面的變數,用圖來看像是這樣子:

所以真正被建立和賦值的是 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)