ES6 變數的解構賦值
變數的解構賦值
解構賦值的規則是:只要等號右邊的值不是物件或陣列,就先將其轉為物件。
- 模式匹配:只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值。
- 不完全解構:等號左邊的模式,只匹配一部分等號右邊的陣列。這種情況下,解構依然可以成功。
// 模式匹配 let [a, [b, c], d] = [1, [2, 3], 4]; // a=1 b=2 c=3 d=4 let [x, ,y] = [1, 2, 3]; // x=1 y=3 // 不完全解構 let [i, j] = [1, 2, 3]; // i=1 j=2 // 解構不成功 let [a, b] = [1]; // a=1 b=undefined(b解構不成功) let [x] = []; // a=undefined(x解構不成功)
如果解構不成功,變數的值就等於 undefined
如果等號的右邊不是陣列(或者嚴格地說,不是可遍歷的結構)那麼將會報錯。
// 報錯
let [x] = 1;
let [x] = false;
let [x] = NaN;
let [x] = undefined;
let [x] = null;
let [x] = {};
上面的語句都會報錯,因為等號右邊的值,要麼轉為物件以後不具備 Iterator 介面(前五個表示式),要麼本身就不具備 Iterator 介面(最後一個表示式)。
let { prop: x } = undefined; // 報錯 let { prop: y } = null; // 報錯
undefined 和 null 無法轉為物件,對其解構會報錯。
let [x, y, z] = new Set(['a', 'b', 'c']); // x: "a"
事實上,只要某種資料結構具有 Iterator 介面,都可以採用陣列形式的解構賦值。
解構賦值允許指定預設值,當嚴格等於 undefined 時預設值才生效。
let [x, y] = [1]; // x=1 y=undefined let [x, y = 3] = [1]; // x=1 y=3(y 解構不成功,等於 undefined,取預設值) let [x = 1] = [null]; // x=null(null !=== undefined,預設值不生效) let [x = 1, y = x] = []; // x=1 y=x=1 let [x = y, y = 1] = []; // 報錯,x 使用變數 y 作為預設值,但是變數 y 還未宣告 let { x: y = 3 } = {}; // y=3(y 解構不成功,等於 undefined,取預設值) let { x: y = 3 } = { x: 5 }; // y=5
預設值可以引用解構賦值的其他變數,但該變數必須已經宣告。
以上最後兩個物件解構,其中 x 只是模式,不是變數。
陣列解構
陣列解構是根據索引匹配的。
let [x, y] = [1, 2]; // x=1 y=2
物件解構
物件解構是根據屬性名匹配的。物件的解構賦值可以取到繼承的屬性。
let { name, age } = { name: '張三', age: 18 }; // name='張三' age=18
// 其實這是縮寫,實際上是:let { name: name, age: age } = { name: '張三', age: 18 };
let { name: firstName, familyName: lastName } = { name: 'f', familyName:'l' }; // firstName="f" lastName="l" 如果列印 name 和 familyName 會報錯:is not defined
物件的解構賦值:是先找到同名屬性,然後再賦給對應的變數。真正被賦值的是後者,而不是前者。
注意:以上第一個例子是縮寫形式。第二個例子中 name, familyName 是模式,不是變數,因此不會被賦值。
// 在 obj 物件中定義了加一、減一兩個方法
let obj = {
add: (num) => {
return ++num;
},
reduce: (num) => {
return --num;
}
}
// 通過呼叫 obj 中的方法計算 5+1 和 5-1 的值
let { add, reduce } = obj;
console.log(`5+1=${add(5)}; 5-1=${reduce(5)}`); // 5+1=6; 5-1=4
物件的解構賦值,可以很方便地將現有物件的方法,賦值到某個變數。
let { obj: { name } } = { age: 18 }; // 報錯(obj = undefined)
如果解構模式是巢狀的物件,而且子物件所在的父屬性不存在,那麼將會報錯。
字串解構
字串也可以解構賦值,此時被轉為一個類似於陣列的物件。
let [a, b] = "Hi"; // a=H b=i
let { length: len } = "Hi"; // len=2
類似陣列的物件都有一個 length 屬性,因此還可以對這個屬性解構賦值。
數值和布林值的解構
解構時,會先轉為物件。
函式引數的解構
函式引數,也可以解構賦值。undefined 會觸發函式引數的預設值。
function add([x, y]) {
return x + y;
}
這個函式引數是一個數組,在傳入引數的那一刻,已經對陣列進行解構,獲取了 x 和 y 的值。
const arr = [[1, 2], [3, 4]];
const rs = arr.map(([x, y]) => {
console.log("x", x, "y", y); // x=1 y=2; x=3 y=4
return x + y;
});
console.log(rs); // [3, 7] length: 2
這是對一個二維陣列的遍歷,二維陣列中每一個item都被解構賦值給 x, y
item = [1, 2],於是 x = 1, y = 2, x + y = 3
item = [3, 4],於是 x = 3, y = 4, x + y = 7
function add({ x = 1, y = 1 } = {}) {
console.log(x, y);
return x + y;
}
const obj = { x: 3, y: 1 };
console.log( add(obj) ); // 4 (x = 3, y = 1)
console.log( add({}) ); // 2(傳入的引數是一個空物件,於是x = 1, y = 1)
以上add函式引數是一個物件,如果傳入的引數是一個空物件,當對其解構時,x = undefined, y = undefined, 於是 x, y 取其預設值(x = 1, y = 1)。
function reduce({ x, y } = { x: 0, y: 0 }) {
console.log(x, y);
return x - y;
}
const obj2 = {};
console.log(reduce(obj)); // NaN (x = undefined, y = undefined)
以上是為reduce函式引數指定預設值,而不是為變數 x 和 y 指定預設值,所以當傳入一個空物件時,不會觸發引數預設值,於是 x = undefined, y = undefined (在傳入的物件中未定義 x, y)。
解構賦值的注意點
可以使用圓括號的情況只有一種:賦值語句的非模式部分,可以使用圓括號。變數宣告語句、函式引數、賦值語句的模式都不能使用圓括號。
let [(a)] = [3]; // 報錯,變數宣告語句不能使用圓括號
[(i)] = [2]; // 正確,這是賦值語句,其圓括號都不屬於模式的一部分
({ p: (d) } = {}); // 正確,解釋同上