1. 程式人生 > 其它 >ES6 變數的解構賦值

ES6 變數的解構賦值

變數的解構賦值

陣列

物件

字串

Number / Boolean

函式引數

解構賦值的注意點

解構賦值的規則是:只要等號右邊的值不是物件或陣列,就先將其轉為物件。

  1. 模式匹配:只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值。
  2. 不完全解構:等號左邊的模式,只匹配一部分等號右邊的陣列。這種情況下,解構依然可以成功。
// 模式匹配
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) } = {}); // 正確,解釋同上