ES6學習 第二章 變數的解構賦值
- 2021 年 11 月 24 日
- 筆記
- ECMAScript 6
前言
該篇筆記是第二篇 變數的解構賦值。
這一章原文鏈接: 變數的解構賦值
解構賦值
ES6 允許按照一定模式,從數組和對象中提取值,對變數進行賦值,這被稱為解構(Destructuring)。
解構賦值是對賦值運算符的擴展。
這是一種針對數組或者對象進行模式匹配,然後對其中的變數進行賦值。
在程式碼書寫上簡潔且易讀,語義更加清晰明了;也方便了複雜對象中數據欄位獲取。
數組的解構賦值
為變數賦值。
let sample1 = 1;
let sample2 = 2;
let sample3 = 3;
上面程式碼使用ES6的數組解構賦值可以寫成下面這樣。從數組中提取值,按照對應位置,對變數賦值。
let [sample1, sample2, sample3] = [1, 2, 3];
console.log(sample1, sample2, sample3); // 1, 2, 3
這種寫法屬於「模式匹配」,只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值。
上面程式碼在聲明變數同時進行了賦值,ES6也可以先聲明變數再進行解構賦值,
let sample1, sample2, sample3; // 先申明
[sample1, sample2, sample3] = [1, 2, 3];
console.log(sample1, sample2, sample3); // 1, 2, 3
數組解構
有幾種情況
- 成功解構
- 完全解構賦值
- 不完全解構賦值
- 嵌套數組解構賦值
- 變數聲明並賦值解構
- 先聲明變數再進行解構賦值
- 不成功解構
注意:
- 數組形式解構賦值 等號右邊必須為可遍歷結構,也就是說具有
Iterator
介面的數據結構。 - 數組形式解構賦值 需要按照對應位置,對對象賦值。
- 數組形式解構賦值 不成功變數的值等於
undefined
。 - 數組形式解構賦值 的數組可以是多維數組。
- 數組形式解構賦值 不用將等號右邊的數組全部解構出來。
- 數組形式解構賦值 允許等號左邊模式不全部匹配等號右邊的數組。
// 解構成功
let [sample1, [sample2, sample3]] = [1, [2, 3]];
console.log(sample1, sample2, sample3); // 1, 2, 3
// 解構不成功,變數的值就等於undefined。
let [sample] = []; // sample 的值為 undefined
let [sample1, sample2] = [1]; // sample2 的值為 undefined
// 如果等號右邊不是數組,也就是不可遍歷結構,將會報錯
let [sample] = 1; // 直接報錯,因為等號右邊的值是個字面量不可遍歷
let sample1, sample2, sample3, sampleN; // 先聲明變數再進行解構賦值
[[sample1, sample2], sample3, ...sampleN] = [[1, 2], 3, 4, 5]; // 嵌套數組
console.log(sample1, sample2, sample3, sampleN); // 1 2 3 [4, 5]
let [sample11, sample12] = [1, 2, 3]; // 變數聲明並賦值解構
let [sample21, ,sample23] = [1, 2, 3];
console.log(sample11, sample12); // 不完全解構 1 2
console.log(sample21, sample23); // 不完全解構 1 3
默認值
當你不想從數組中解構出的值為undefined
,解構賦值允許指定默認值。
注意:
- 當一個數組成員嚴格等於
undefined
,默認值才會生效。 - 默認值不一定為字面量,也可以引用解構賦值的其他變數,但該變數必須已聲明。
- 默認值是一個表達式,那麼這個表達式是惰性求值的,即只有在用到的時候,才會求值。
// 當 值 不存在時,默認值生效
let [sample1 = 10, sample2 = 11] = [2];
console.log(sample1, sample2); // 2 11
// 當 值 === undefined 時,默認值才會生效
let [sample10 = 10, sample11 = 11, sample13 = 12] = [undefined, "", null];
console.log(sample10, sample11, sample13); // 10, , null,
let [sample21 = 1, sample22 = sample21] = [];
console.log(sample21, sample22);
let sample30 = 31;
let [sample31 = sample30, sample32 = 3] = [];
console.log(sample31, sample32);
對象的解構賦值
對象解構
數組的解構賦值與對象的解構賦值有很大區別
注意:
- 對象的解構賦值不再和順序有關,是與變數名有關,變數必須與屬性同名,才能取到正確的值。
- 對象的解構賦值是根據對象key值進行匹配。
- 如果解構不成功,那麼值為
undefined
。 - 和數組解構賦值一樣,可以對嵌套結構的對象進行解構賦值。
表達式等號左右兩邊,都要有互相匹配的屬性值,才能將右邊匹配成功key
的value
值賦值給左邊相對應key
的value
,左邊的value
值作為變數。
let { sample: sample, sample1: sample2 } = { sample: 10, sample1: 11 }
console.log(sample); // 10
console.log(sample1) // 報錯 sample1 is not defined
console.log(sample2) // 11
ES6 對對象進行了擴展,對象里的屬性與value值可以簡寫(以後會提到,目前只要知道可以這樣用),
ES6 允許在大括弧裡面,直接寫入變數和函數,作為對象的屬性和方法。這樣的書寫更加簡潔。
當屬性名與值或方法名相同時,可以簡寫為一個。
const { log: log } = console; // 可以解構現有對象
log('hello'); // hello
// 上面程式碼可以簡寫為下面
const { log } = console;
log('hello') // hello
在嵌套的對象中使用解構賦值。
// 多重嵌套,還可以嵌套數組
let obj = {
sample: [
'Hello',
{ sample1: 'World' }
]
};
let { sample: [sample, { sample1 }] } = obj;
console.log(sample,sample1); // Hello World
// 或者像這樣?給數組或對象添加新內容
let obj = {};
let arr = [];
({ sample: obj.a, sample1: arr[0] } = { sample: 123, sample1: true });
console.log(obj); // {a: 123}
console.log(arr); // [true]
默認值
和數組解構賦值默認值類似,對象的默認值
注意:
- 當一個對象的屬性嚴格等於
undefined
,默認值才會生效。 - 默認值不一定為字面量,也可以引用解構賦值的其他變數,但該變數必須已聲明。
// 當 對象的屬性值 不存在時,默認值生效
let {sample1 = 10, sample2 = 11} = {sample1:2};
console.log(sample1, sample2); // 2, 11
//當 對象的屬性值 === undefined 時,默認值才會生效
let { sample10 = 10, sample11 = 11, sample12 = 12 } =
{ sample10: "", sample11: undefined, sample12: null };
console.log(sample10, sample11, sample12); // 11, , null
// 默認值為變數
let {sample21 = 1, sample22 = sample21} = {};
console.log(sample21, sample22); // 1, 1
let sample30 = 31;
let {sample31 = sample30, sample32 = 3} = {};
console.log(sample31, sample32); // 31, 3
注意
- 如果要將一個已經聲明的變數用於解構賦值,必須非常小心。
- 解構賦值允許等號左邊的模式之中,不放置任何變數名。
- 由於數組本質是特殊的對象,因此可以對數組進行對象屬性的解構。
// 1. 語法錯誤
let sample;
{sample:sample} = {sample: 1}; // 直接報錯
/*
因為 JavaScript 引擎會將{sample}理解成一個程式碼塊,從而發生語法錯誤。只有不將大括弧寫在行首,避免 JavaScript 將其解釋為程式碼塊,才能解決這個問題。
*/
// 2. 雖然無意義但是合法
({} = [true, false]);
({} = 'abc');
({} = []);
// 3. 數組使用對象的解構賦值,
let { 0: sample0, 1: sample1 } = [1, 2];
console.log(sample0, sample1); // 1, 2
字元串的解構賦值
字元串也可以解構賦值。
當字元串進行解構賦值的時候,字元串被轉換成了一個類數組。
類數組也有length
屬性,所以還可以通過這個方式獲取字元串的長度
let { length } = 'sample';
console.log(length); // 6
數值和布爾值的解構賦值
解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其轉為對象。
由於**undefined**
和**null**
無法轉為對象,所以對它們進行解構賦值,都會報錯。
// 數值與布爾值 進行解構賦值
let { toLocaleString: sampleNum } = 111;
console.log(sampleNum === Number.prototype.toLocaleString);
let { toString: sampleBol } = true;
console.log(sampleBol === Boolean.prototype.toString);
// undefined 和 null 進行解構賦值
let { prop: x } = undefined; // 直接報錯 TypeError
let { prop: y } = null; // 直接報錯 TypeError
函數參數的解構賦值
函數參數進行解構賦值,函數參數需要為可解構賦值的解構。
// 參數為數組時,參數在傳入函數的時候,數組參數就被解構成變數 sample1 和 sample2 。
function addNumberArr([sample1, sample2]) {
return sample1 + sample2;
}
console.log(addNumberArr([4, 4])); // 8
// 參數為對象時,函數通過對這個參數進行解構 得到變數 sample1 和 sample2 的值。
function addNumberObj({ sample1, sample2 }) {
return sample1 + sample2;
}
console.log(addNumberObj({ sample1: 10, sample2: 20 })); // 30
函數參數的解構也可以使用默認值。
// 函數通過對這個參數進行解構,得到變數sample1和sample2的值。
// 如果解構失敗,sample1和sample2等於默認值。
function sample({ sample1 = 0, sample2 = 0 } = {}) {
console.log([sample1, sample2])
return [sample1, sample2];
}
sample({ sample1: 1, sample2: 2 }); // [1, 2]
sample({ sample1: 1 }); // [1, 0]
sample({}); // [0, 0]
sample(); // [0, 0]
圓括弧問題
什麼是圓括弧問題呢?
原來在編譯器中,無法在解析到等號之前識別一個式子為表達式還是解構賦值等號左邊部分,也就是模式匹配中左邊的
key
(屬性)值。那麼如果識別到了圓括弧後,編譯器該按照什麼方式去處理圓括弧呢?
ES6 規定,只要有可能導致解構的歧義,就不得使用圓括弧。
阮一峰老師建議只要有可能,就不要在模式中放置圓括弧。
注意:以下三種情況不能使用圓括弧
- 變數聲明語句
- 函數參數也屬於變數聲明,因此不能帶有圓括弧。
- 賦值語句的模式
上面三種情況使用圓括弧直接報錯。
// 1. 變數聲明語句
let [(sample)] =[1]; // 直接報錯
let { sample: ({ sample1: sample1 }) } = { sample: { sample1: 2 } }; // 直接報錯
// 2. 函數參數也屬於變數聲明,因此不能帶有圓括弧。
function sampleFn([(sample)]) { return sample; } // 直接報錯
// 3. 賦值語句的模式
({ sample2: sample21 }) = { sample2: 42 }; // 直接報錯
[({ sample3: sample31 }), { sample4: sample41 }] = [{}, {}];
只有一種情況可以使用圓括弧
不能有聲明語句,賦值語句的非模式(屬性名)部分,可以使用圓括弧。
({ sample: (sample) } = {}); // 正確
console.log(sample); // undefined
解構賦值的用途
解構賦值主要還是用在對象和數組上,
- 交換變數的值
不用臨時變數,怎麼交換兩個變數的值?可以使用解構賦值簡單實現。
// 數組的解構賦值
let sample1 = 2;
let sample2 = 4;
[sample1, sample2] = [sample2, sample1];
console.log(sample1, sample2); // 4 , 2
- 函數多個返回值的解構賦值
// 解構函數返回的對象
function sample() {
return [1, 2, 3];
}
let [sample1, sample2, sample3] = sample();
console.log(sample1, sample2, sample3); // 1, 2, 3
// 解構函數返回的對象
function sample() {
return {
sample11: 1,
sample12: 2,
sample13: 3,
};
}
let {sample11, sample12, sample13} = sample();
console.log(sample11, sample12, sample13); // 1, 2, 3
- 提取JSON數據
可以使用解構賦值一次性提取多個數據
let sampleJson = {
id: 11,
status: false,
data: [{ name: 'name1' }, { name: 'name2' }]
}
let { id, status, data } = sampleJson;
console.log(id, status, data);// 11, false, [{ name: 'name1' }, { name: 'name2' }]
- 輸入模組的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map"); // 示例程式碼