前言

該篇筆記是第二篇 變量的解構賦值。
這一章原文鏈接: 變量的解構賦值

解構賦值

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 2console.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
  • 和數組解構賦值一樣,可以對嵌套結構的對象進行解構賦值。

表達式等號左右兩邊,都要有互相匹配的屬性值,才能將右邊匹配成功keyvalue值賦值給左邊相對應keyvalue,左邊的value值作為變量。

let { sample: sample, sample1: sample2 } = { sample: 10, sample1: 11 }console.log(sample); // 10console.log(sample1) // 報錯 sample1 is not definedconsole.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, 1let sample30 = 31;let {sample31 = sample30, sample32 = 3} = {};console.log(sample31, sample32); // 31, 3

注意

  1. 如果要將一個已經聲明的變量用于解構賦值,必須非常小心。
  2. 解構賦值允許等號左邊的模式之中,不放置任何變量名。
  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; // 直接報錯 TypeErrorlet { 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. 變量聲明語句
  2. 函數參數也屬于變量聲明,因此不能帶有圓括號。
  3. 賦值語句的模式

上面三種情況使用圓括號直接報錯。

// 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

解構賦值的用途

解構賦值主要還是用在對象和數組上,

  1. 交換變量的值

不用臨時變量,怎么交換兩個變量的值?可以使用解構賦值簡單實現。

// 數組的解構賦值let sample1 = 2;let sample2 = 4;[sample1, sample2] = [sample2, sample1];console.log(sample1, sample2); // 4 , 2
  1. 函數多個返回值的解構賦值
// 解構函數返回的對象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
  1. 提取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 }]
  1. 輸入模塊的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map"); // 示例代碼