第3章 變數的解構賦值
3.1 陣列的解構賦值
3.1.1 基本用法
ES6 允許按照一定模式從陣列和物件中提取值,然後對變數進行賦值,這被稱為解構(Destructuring)。
以前,為變數賦值只能直接指定值。
// hzh.js
let hzh1 = 1;
let hzh2 = 2;
let hzh3 = 3;
ES6允許寫成下面這樣。
// hzh.js
let [hzh1, hzh2, hzh3] = [1, 2, 3];
上面的程式碼表示,可以從陣列中提取值,按照對應位置對變數賦值。
本質上,這種寫法屬於“模式匹配”,只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值。下面是一些使用巢狀陣列進行解構的例子。
// hzh.js
let [hzh1, [[hzh2], hzh3]] =[ 1,[[2], 3] ];
console.log("hzh1 = " + hzh1);
console.log("hzh2 = "+ hzh2);
console.log("hzh3 = " + hzh3);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = 1
hzh2 = 2
hzh3 = 3
[Done] exited with code=0 in 0.173 seconds
// hzh.js let [ , , third ,] = ["尤雨溪", "人世間", "黃子涵"]; console.log("third = " + third);
[Running] node "e:\HMV\Babel\hzh.js"
third = 黃子涵
[Done] exited with code=0 in 0.83 seconds
// hzh.js
let [hzh1, , hzh3] = [1, 2, 3];
console.log("hzh1 = " + hzh1);
console.log("hzh3 = " + hzh3);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = 1
hzh3 = 3
[Done] exited with code=0 in 0.205 seconds
// hzh.js let [hzh1, ...hzh2] = [1, 2, 3, 4]; console.log("輸出hzh1的值:"); console.log(hzh1); console.log("輸出hzh2的值:"); console.log(hzh2);
[Running] node "e:\HMV\Babel\hzh.js"
輸出hzh1的值:
1
輸出hzh2的值:
[ 2, 3, 4 ]
[Done] exited with code=0 in 0.179 seconds
// hzh.js
let [hzh1, hzh2, ...hzh3] = ['黃子涵'];
console.log("輸出hzh1的值:");
console.log(hzh1);
console.log("輸出hzh2的值:");
console.log(hzh2);
console.log("輸出hzh3的值:");
console.log(hzh3);
[Running] node "e:\HMV\Babel\hzh.js"
輸出hzh1的值:
黃子涵
輸出hzh2的值:
undefined
輸出hzh3的值:
[]
[Done] exited with code=0 in 0.201 seconds
如果解構不成功,變數的值就等於undefined。
// hzh.js
let [hzh1] = [];
let [hzh2, hzh3] = [1];
console.log("hzh1 = "+ hzh1);
console.log("hzh2 = "+ hzh2);
console.log("hzh3 = "+ hzh3);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = undefined
hzh2 = 1
hzh3 = undefined
[Done] exited with code=0 in 0.198 seconds
以上兩種情況都屬於解構不成功,hzh和hzh3的值都會等於undefined。
另一種情況是不完全解構,即等號左邊的模式只匹配一部分的等號右邊的陣列。這種情況下,解構依然可以成功。
// hzh.js
let [hzh1, hzh2] = [1, 2, 3];
let [hzh3, [hzh4], hzh5] = [3, [4, '不解構'], 5];
console.log("hzh1 = "+ hzh1);
console.log("hzh2 = "+ hzh2);
console.log("hzh3 = "+ hzh3);
console.log("hzh4 = "+ hzh4);
console.log("hzh5 = "+ hzh5);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = 1
hzh2 = 2
hzh3 = 3
hzh4 = 4
hzh5 = 5
[Done] exited with code=0 in 0.226 seconds
上面兩個例子都屬於不完全解構,但是可以成功。
如果等號的右邊不是陣列(或者嚴格來說不是可遍歷的結構),那麼將會報錯。
// hzh.js
// 報錯的例子
let [hzh1] = 1;
console.log(hzh1);
[Running] node "e:\HMV\Babel\hzh.js"
e:\HMV\Babel\hzh.js:4
let [hzh1] = 1;
^
TypeError: 1 is not iterable
at Object.<anonymous> (e:\HMV\Babel\hzh.js:4:14)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.168 seconds
// hzh.js
// 報錯的例子
let [hzh2] = false;
console.log(hzh2);
[Running] node "e:\HMV\Babel\hzh.js"
e:\HMV\Babel\hzh.js:4
let [hzh2] = false;
^
TypeError: false is not iterable
at Object.<anonymous> (e:\HMV\Babel\hzh.js:4:14)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.169 seconds
// hzh.js
// 報錯的例子
let [hzh3] = NaN;
console.log(hzh3);
[Running] node "e:\HMV\Babel\hzh.js"
e:\HMV\Babel\hzh.js:4
let [hzh3] = NaN;
^
TypeError: NaN is not iterable
at Object.<anonymous> (e:\HMV\Babel\hzh.js:4:14)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.194 seconds
// hzh.js
// 報錯的例子
let [hzh4] = undefined;
console.log(hzh4);
[Running] node "e:\HMV\Babel\hzh.js"
e:\HMV\Babel\hzh.js:4
let [hzh4] = undefined;
^
TypeError: undefined is not iterable
at Object.<anonymous> (e:\HMV\Babel\hzh.js:4:14)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.186 seconds
// hzh.js
// 報錯的例子
let [hzh5] = null;
console.log(hzh5);
[Running] node "e:\HMV\Babel\tempCodeRunnerFile.js"
e:\HMV\Babel\tempCodeRunnerFile.js:4
let [hzh5] = null;
^
TypeError: null is not iterable
at Object.<anonymous> (e:\HMV\Babel\tempCodeRunnerFile.js:4:14)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.169 seconds
// hzh.js
// 報錯的例子
let [hzh6] = {};
console.log(hzh6);
[Running] node "e:\HMV\Babel\hzh.js"
e:\HMV\Babel\hzh.js:4
let [hzh6] = {};
^
TypeError: {} is not iterable
at Object.<anonymous> (e:\HMV\Babel\hzh.js:4:14)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.17 seconds
上面的語句都會報錯,因為等號右邊的值或是轉為物件以後不具備 Iterator 介面(前五個表示式),或是本身就不具備 Iterator 介面(最後一個表示式)。
對於 Set 結構,也可以使用陣列的解構賦值。
// hzh.js
let [hzh1, hzh2, hzh3] = new Set(['黃子涵', '尤雨溪', '人世間']);
console.log("hzh1 = " + hzh1);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = 黃子涵
[Done] exited with code=0 in 0.506 seconds
事實上,只要某種資料結構具有 Iterator 介面,都可以採用陣列形式的解構賦值。
// hzh.js
function* fibs() {
let hzh1 = 1;
let hzh2 = 2;
while(true) {
yield hzh1;
[hzh1, hzh2] = [hzh2, hzh1 + hzh2];
}
}
let [first, second, third, fourth, fifth, sixth] = fibs();
console.log(sixth);
[Running] node "e:\HMV\Babel\hzh.js"
13
[Done] exited with code=0 in 0.176 seconds
上面的程式碼中,fibs 是一個 Generator 函式,原生具有 Iterator 介面。解構賦值會依次從這個介面中獲取值。
上面關於fibs函式這部分不是很懂。
3.1.2 預設值
// hzh.js
let [hzh1 = true] = [];
console.log("hzh1 = " + hzh1);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = true
[Done] exited with code=0 in 0.194 seconds
// hzh.js
let [hzh2, hzh3 = 'b'] = ['黃子涵是帥哥!'];
let [hzh4, hzh5 = 'c'] = ['黃子涵是靚仔!', undefined];
console.log("hzh2 = " + hzh2);
console.log("hzh3 = " + hzh3);
console.log("hzh4 = " + hzh4);
console.log("hzh5 = " + hzh5);
[Running] node "e:\HMV\Babel\hzh.js"
hzh2 = 黃子涵是帥哥!
hzh3 = b
hzh4 = 黃子涵是靚仔!
hzh5 = c
[Done] exited with code=0 in 0.192 seconds
注意
ES6 內部使用嚴格相等運算子(===)判斷一個位置是否有值。所以,如果一個數組成員不嚴格等於undefined,預設值是不會生效。
// hzh.js
let [hzh1 = 1] = [undefined];
let [hzh2 = 2] = [null];
console.log("hzh1 = " + hzh1);
console.log("hzh2 = " + hzh2);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = 1
hzh2 = null
[Done] exited with code=0 in 0.576 seconds
上面的程式碼,如果一個數組成員,預設值就不會生效,因為 null 不嚴格相等 undefined。
如果預設值是一個表示式,那麼這個表示式是惰性求值,即只有在用到時才會求值。
// hzh.js
function huangzihan() {
console.log('黃子涵');
}
let [hzh = huangzihan()] = [1];
[Running] node "e:\HMV\Babel\hzh.js"
[Done] exited with code=0 in 0.594 seconds
上面的程式碼中,因為 x 能取到值,所以函式 f 根本不會執行。上面的程式碼其實等價於下面的程式碼。
// hzh.js
let hzh;
if([1][0] === undefined) {
hzh = huangzihan();
} else {
hzh = [1][0];
}
[Running] node "e:\HMV\Babel\tempCodeRunnerFile.js"
[Done] exited with code=0 in 0.194 seconds
預設值可以引用解構賦值的其他變數,但該變數必須已經宣告。
// hzh.js
let [hzh1 = 1, hzh2 = hzh1] = [];
console.log("hzh1 = " + hzh1);
console.log("hzh2 = " + hzh2);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = 1
hzh2 = 1
[Done] exited with code=0 in 0.176 seconds
// hzh.js
let [hzh3 = 3, hzh4 = hzh3] = [2];
console.log("hzh3 = " + hzh3);
console.log("hzh4 = " + hzh4);
[Running] node "e:\HMV\Babel\hzh.js"
hzh3 = 2
hzh4 = 2
[Done] exited with code=0 in 0.196 seconds
// hzh.js
let [hzh5 = 5, hzh6 = hzh5] = [1, 2];
console.log("hzh5 = " + hzh5);
console.log("hzh6 = " + hzh6);
[Running] node "e:\HMV\Babel\hzh.js"
hzh5 = 1
hzh6 = 2
[Done] exited with code=0 in 0.179 seconds
// hzh.js
let [hzh7 = hzh8, hzh8 = 8] = [];
console.log("hzh7 = " + hzh7);
console.log("hzh8 = " + hzh8);
[Running] node "e:\HMV\Babel\tempCodeRunnerFile.js"
e:\HMV\Babel\tempCodeRunnerFile.js:3
let [hzh7 = hzh8, hzh8 = 8] = [];
^
ReferenceError: Cannot access 'hzh8' before initialization
at Object.<anonymous> (e:\HMV\Babel\tempCodeRunnerFile.js:3:13)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.23 seconds
上面最後一個表示式之所以會報錯,是因為 hzh7 用到預設值 hzh8 時,hzh8 還沒有宣告。
3.2 物件的解構賦值
解構不僅可以用於陣列,還可以用於物件。
// hzh.js
let { hzh1, hzh2 } = { hzh1: "黃子涵", hzh2: "尤雨溪" };
console.log("hzh1 = " + hzh1);
console.log("hzh2 = " + hzh2);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = 黃子涵
hzh2 = 尤雨溪
[Done] exited with code=0 in 0.229 seconds
物件的解構與陣列有一個重要的不同。陣列的元素是按次序排列的,變數的取值是由它的位置決定的;而物件的屬性沒有次序,變數必須與屬性同名才能取到正確的值。
// hzh.js
let { hzh2, hzh1 } = { hzh1: "黃子涵", hzh2: "尤雨溪" };
console.log("hzh1 = " + hzh1);
console.log("hzh2 = " + hzh2);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = 黃子涵
hzh2 = 尤雨溪
[Done] exited with code=0 in 0.196 seconds
// hzh.js
let { hzh2 } = { hzh1: "黃子涵", hzh2: "尤雨溪" };
console.log("hzh3 = " + hzh3);
[Running] node "e:\HMV\Babel\hzh.js"
e:\HMV\Babel\hzh.js:4
console.log("hzh3 = " + hzh3);
^
ReferenceError: hzh3 is not defined
at Object.<anonymous> (e:\HMV\Babel\hzh.js:4:25)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.171 seconds
上面程式碼的第一個例子中,等號左邊的兩個變數的次序與等號右邊兩個同名屬性的次序不一致,但是對取值完全沒有影響。第二個例子的變數沒有對應的同名屬性,導致取不到值,最後等於 undefined 。
如果變數名與屬性名不一致,必須寫成下面這樣
// hzh.js
let { hzh1: hzh3 } = { hzh1: "黃子涵", hzh2: "尤雨溪" };
console.log("hzh3 = " + hzh3);
[Running] node "e:\HMV\Babel\hzh.js"
hzh3 = 黃子涵
[Done] exited with code=0 in 0.203 seconds
// hzh.js
let huangzihan = { hzh1: '黃子涵', hzh2: '尤雨溪'};
let { hzh1: h, hzh2: z} = huangzihan;
console.log("hzh1物件的h屬性:" + h);
console.log("hzh2物件的z屬性:" + z);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1物件的h屬性:黃子涵
hzh2物件的z屬性:尤雨溪
[Done] exited with code=0 in 0.194 seconds
實際上說明,物件的解構賦值是下面形式的簡寫。
// hzh.js
let { hzh1: hzh1, hzh2: hzh2} = { hzh1: '黃子涵', hzh2: '尤雨溪'};
console.log("hzh1 = " + hzh1);
console.log("hzh2 = " + hzh2);
[Running] node "e:\HMV\Babel\hzh.js"
hzh1 = 黃子涵
hzh2 = 尤雨溪
[Done] exited with code=0 in 0.2 seconds
也就是說,物件的解構賦值的內部機制是先找到同名屬性,然後再賦值給對應的變數。真正被賦值的是後者,而不是前者。
// hzh.js
let { hzh1: hzh3} = { hzh1: '黃子涵', hzh2: '尤雨溪'};
console.log("hzh3 = " + hzh3);
console.log("hzh1 = " + hzh1);
[Running] node "e:\HMV\Babel\hzh.js"
hzh3 = 黃子涵
e:\HMV\Babel\hzh.js:5
console.log("hzh1 = " + hzh1);
^
ReferenceError: hzh1 is not defined
at Object.<anonymous> (e:\HMV\Babel\hzh.js:5:25)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
at internal/main/run_main_module.js:17:47
[Done] exited with code=1 in 0.176 seconds
上面的程式碼中,hzh1是匹配的模式,hzh3才是變數。真正被賦值的是變數 hzh3,而不是模式 hzh1。
與陣列一樣,解構也可以用於巢狀結構的物件。
注意,這時p是模式,不是變數,因此不會被賦值。如果p也要作為變數賦值,可以寫成下面這樣。
上面的程式碼有三次解構賦值,分別是對 loc、start、line 三個屬
性的解構賦值。需要注意的是,最後一次對line屬性的解構賦值
之中,只有line是變數,loc和start都是模式,不是變數。
下面是巢狀賦值的例子。