ES6 解構與賦值

認識解構與賦值 (Destructuring)

解構賦值可以簡單先以這張圖做初步認識,簡而言之就是將右邊的值,鏡射到左方的變數,不過要注意這邊的鏡射不會有任何順序或方向的改變。

圖片來源: 六角學院線上課程

陣列

傳統上如果我們想將陣列的值取出,賦予給另一個新變數,我們會這麼做。

1
2
3
4
let family = ['Dylan', 'Eva', 'John']
let son = family[0]
let daugter = family[1]
let father = family[2]

在 ES6 的做法中,我們可以用 解構賦值 的方式,使可讀性提高。

1
2
3
4
5
6
let family = ['Dylan', 'Eva', 'John']
let [son, daughter, father] = family

console.log(son) // Dylan
console.log(daughter) // Eva
console.log(father) // John

這個例子具象化如下,就像一開始那張 鏡射 的圖片概念一樣,將右邊的值依序對應,賦予給左方的變數。

1
let [son, daughter, father] = ['Dylan', 'Eva', 'John']

如果賦值的對象或者被賦值的變數數量不等,會出錯嗎?

  • 賦值對象數量較少的情況
1
2
3
4
5
6
let family = ['Dylan', 'Eva']
let [son, daughter, father] = family

console.log(son) // Dylan
console.log(daughter) // Eva
console.log(father) // undefined,相當於宣告 let father;
  • 被賦值變數數量較少的情況
1
2
3
4
5
6
let family = ['Dylan', 'Eva', 'John']
let [son, daughter] = family

console.log(son) // Dylan
console.log(daughter) // Eva
//依序排入,不會有影響。
  • 跳過賦值對象內的某的值
1
2
3
4
5
let family = ['Dylan', 'Eva', 'John']
let [son, , father] = family // 想跳過的位置不要宣告變數即可,但注意逗號還是要保留。

console.log(son) // Dylan
console.log(father) // John

字串

這邊要做的是拆開字串內的每個字元,並依數量賦予給不同的變數。

1
2
3
4
5
6
7
let str = '字元拆解';
let [A, B, C, D] = str;

console.log(A) // 字
console.log(B) // 元
console.log(C) // 拆
console.log(D) // 解

使用解構賦值將兩個變數的值互換

ES5要實作兩個變數值互換,需要宣告第三個變數作中繼,但是ES6的解構賦值可以不需要,因為他們的交換時的資料傳遞是同時進行的,如下程式碼。

1
2
3
let son = 'Dylan';
let daughter = 'Eva';
[son, daughter] = [daughter, son];

註:son和daughter變數已經存在,所以不需要在前方做let宣告,這裡只是同步的將兩個變數值做交換

額外補充

在查資料時,發現一個很有意思的ES5同步變數值交換方式,當中用到了克服JS奇怪的部分提過的運算子的優先性和相依性的觀念,這部分的觀念要非常清楚,才能真正理解這行程式碼的精隨。

1
2
3
4
5
6
let a = 'a';
let b = 'b';
b = [a, a = b][0]; // 這樣寫也行 a = [b][b = a,0];

console.log(a) //b
console.log(b) //a

Stackoverflow討論

物件

1
2
3
4
5
let data = {
naruto: '鳴人',
sasuke: '佐助',
sakura: '小櫻'
}

取出物件內的其中一個值,並創造變數賦予該值。

等於運算子的相依性是由右至左,這邊從=的右方data開始看。data是目標物件,再來因為我們寫了{naruto},JS會去等號右邊物件data裡面找到 naruto屬性,並抓取它的,最後將該值賦予給一個變數naruto

我們得到了一個新變數 naruto,而它的值則是從 data.naruto 抓取。

1
2
3
let {naruto} = data;

console.log(naruto) // 鳴人

如果變數名稱想要自己取,不想用物件屬性的名稱,可以改嗎?

下方程式碼多了一個:,它的右方是自定義的變數名稱,若有撰寫,JS將會把原先該賦予給變數naruto的值,轉而創建一個新變數naruto_uzumaki,並將該值傳遞給它。

如果在 : 右方有定義新變數名稱,則會使用新變數名稱。

1
2
3
4
5
let {naruto: naruto_uzumaki} = data;


console.log(naruto) // Uncaught ReferenceError: naruto is not defined,因為值是被傳入naruto_uzumaki
console.log(naruto_uzumaki) // 鳴人

延伸題目 - 較複雜的物件解構賦值

1
2
3
4
5
let {naruto: naruto_uzumaki, group: [, sakura]} = {naruto: '鳴人', group: ['佐助', '小櫻', '卡卡西']}

// 想想看答案
console.log(naruto_uzumaki)
console.log(sakura)

這樣寫或許更好懂

1
2
3
4
5
6
let data = {
naruto: '鳴人',
group: ['佐助', '小櫻', '卡卡西']
}

let {naruto: naruto_uzumaki, group: [, sakura]} = data

設定預設值

陣列

1
2
3
4
5
let [A = 'Dylan', B = '將被Eva取代的人', C] = [, 'Eva', 'John']

console.log(A) // 沒有傳入參數,以預設值Dylan帶入
console.log(B) // 預設值'將被Eva取代的人'被傳入值'Eva'取代
console.log(C) // John

物件

1
2
3
4
5
6
let {naruto: naruto_uzumaki = '漩渦鳴人', sasuke: Uchiha_sasuke} = {sasuke: '佐助'}

console.log(naruto_uzumaki)
// 漩渦鳴人 - 等號右邊物件沒有naruto屬性,但是在左邊有設定預設值是'漩渦鳴人'。
console.log(Uchiha_sasuke)
// 佐助 - 等號右邊物件有sasuke屬性,值是'佐助',所以可以直接參照到值。另外沒有預設值,即使有也會被帶入的值覆蓋。

資料來源

六角學院 - Vue出一個電商網站