ES6系列入門學習記錄:變量的解構賦值

前言

年都過去半個月了,我終於又從新開始更新了。雖然只是第二篇,可是我會繼續加油努力,必定不會放棄更新的。在文章中如有什麼不妥或者您有更多建議的話,歡迎和期待您給我留言,您的每個留言均可能成爲我進步的助力,十分感謝。那就廢話很少說直接開始吧。es6

概念

解構賦值,顧名思義,就是解開一個結構,而後從裏面拿出值用來給變量賦值賦值。因此解構賦值主要是以數據類型來劃分的。ajax

數組的解構賦值

var [a,b,c] = [1,2,3];
複製代碼

上述代碼算是最簡單的數組解構賦值,其實也能夠看作是數據的另外一種展現。好比上述代碼與下面的代碼實際上是同樣的。json

var a=1,b=2,c=3;
複製代碼

因此解構賦值最主要的做用,是可讓咱們簡化提取值的過程。數組

本質上,這種寫法屬於「模式匹配」,同模式下,左邊的變量就會被賦予對應位置的右邊的值。例如:bash

let [a,[[b],c]] = [1,[[2],3]];
a //1
b //2
c //3
複製代碼

而且只要模式相同,即使部分位置的變量或者值爲空,依舊能夠進行匹配。數據結構

let [a, , b] = [1,2,3];
a //1
b //3

let [a,b,c] = [1,2];
a//1
b//2
c//undefind
複製代碼

當解構不成功,即變量沒有獲得賦值,或者直接賦值undefind時,變量的值就會等於undefindasync

當匹配兩邊的模式相同,且長度不一樣時,此時的解構賦值被稱爲不徹底解構。雖然叫不徹底解構,可是依舊算解構成功的。函數

let [a,b] = [1,2,3];
a //1
b //2
複製代碼

上面一直提到一個前提狀況,那就是模式相同,沒錯,這是比較須要注意的一點。當兩邊模式不一樣時,解構賦值是會報錯的。ui

let [a] = 1;
let [b] = false;
let [c] = {};
複製代碼

在不嚴謹的狀況下,咱們能夠說,當兩邊的數據類型不一樣時,解構賦值會出現報錯。url

只要某種數據結構具備Iterator接口,均可以採用數組形式的解構賦值。Iterator接口最主要的功能是能夠提供遍歷命令for...of使用,不難猜想,其實數組解構賦值是一個將變量遍歷循環,而後一一進行賦值的操做

function* fibs() {
  let a = 0;
  let b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
複製代碼

以上是阮一峯大神ES6入門裏面的實例,因爲我的目前還不清楚具體哪些數據結構具備Iterator接口,因此這裏直接搬運一下。

默認值

let [a=1] = [];
a//1
複製代碼

解構賦值操做時,能夠設置一個默認值,若解構賦值操做室,對應位置上的值爲undefind時,將會給變量賦值默認值。

須要注意的是,ES6內部使用嚴格相等運算符(===)來判斷一個位置是否有值,我通常習慣稱它爲全等符號。因此,與通常的判斷不一樣,這裏只有用於賦值的數組成員的值爲undefined(嚴格等於undefined)時,默認值纔會生效。

let [a=1] = [undefined];
a//1

let [b=1] = [null];
b//null

let [c=1] = [NaN];
c//NaN
複製代碼

使用默認值時,還能夠引用解構賦值的其餘變量,但前提是該變量已聲明。

let [a=1,b=a] = [];
a//1
b//1

let [a=b,b=1] = [];
//ReferenceError: b is not defined

var [a=b,b=1] = [];
a//undefined
b//1
複製代碼

對象的解構賦值

let { a,b } = { a:'1',b:'2'};
a //'1'
b //'2'
複製代碼

對象的解構賦值與數組的解構賦值最大的不一樣之處,在於數組的解構賦值,變量的取值是由位置決定的;而對象的解構賦值,變量的取值是由屬性名來決定的,只有變量與屬性名相同,才能夠取到值。

let { a , b } = { b : '2' , a : '1' };
a //1
b //2

let { a } = { b: '1' , c: '2' };
a//undefined
複製代碼

當變量名與屬性名不一致,卻又須要進行解構賦值時,可使用變量再進行一次解構賦值。

let obj = { a : '1' , b : '2' };
let { a : c , b : d } = obj;
c //'1'
d //'2'
複製代碼

而且,在這過程當中,實際被賦值的,實際上是cd。而ab是模式,起到相似於一箇中介做用,不會被實際賦值。

let { a : b } = { a : '1'};
a //ReferenceError: a is not defined
b //'1'
複製代碼

對象的解構賦值,與數組的解構賦值同樣,也能夠用於嵌套結構的對象。

let a = {
    b : [
        '1',
        { c : '2' }
    ]
};

let {
    b : [
        x ,
        { c }
    ]
} = a;

x // '1'
c // '2'
b // ReferenceError: b is not defined
複製代碼

此時b只是模式,因此沒法被賦值。

默認值

對象的解構賦值也有默認值,默認值的設置方式與數組相同,而不是依舊使用對象的內部寫法。

let { a = 1} = {};
a // 1

let { b : 1 } = {};
//SyntaxError: Invalid destructuring assignment target
複製代碼

默認值生效的條件與數組的解構賦值相同,屬性值必須嚴格等於undefined纔會生效。

let { a = 1 } = { a : undefined };
a //1 

let { b = 1 } = { b : null };
b // null

let { c = 1 } = { c : NaN };
c // NaN
複製代碼

在對嵌套的對象使用解構賦值時,須要注意,若子對象所在的父屬性不存在時,會報錯。這也是我在工做中,發現比較常見的一種報錯,仍是須要多多注意的。特別是在使用多層結構的時候,例如res.data.id

let { a: {b} }  = { c : '1' };
TypeError: Cannot destructure property `b` of 'undefined' or 'null'
複製代碼

在使用對象解構賦值的時候,若是要對已經聲明的變量進行解構賦值,須要當心。

let a;
{a} = {a:1};
//SyntaxError: Unexpected token =
複製代碼

這裏是由於JavaScript引擎會將{a}當作一個代碼塊,從而引起語法錯誤。因此須要避免將大括號寫在行首

let a;
({a} = {a:1});
a // 1
複製代碼

因爲數組的本質是特殊的對象,所以能夠對數組進行對象屬性的解構賦值。

let a = [1, 2, 3];
let {0 : b, 2: c} = a;
b // 1
c // 3
複製代碼

第二行代碼的0和2表明的是數組的位置,能夠簡單理解爲如下代碼:

let a = [1,2,3];
let b =  a[0];
let c =  a[2];
複製代碼

字符串的解構賦值

字符串也能夠進行解構賦值,由於此時字符串被轉換成了一個相似數組的對象。

let [a, b, c, d, e] = 'hello';
a // 'h'
b // 'e'
c // 'l'
d // 'l'
e // 'o'
複製代碼

看到這裏是否是感受這個過程有點眼熟,其實這個過程能夠理解爲如下代碼:

let x = 'hello';
a = x[0];
b = x[1];
c = x[2];
...

複製代碼

數值和布爾值的解構賦值

解構賦值時,若是等號右邊是數值或者布爾值,則會先轉化成對象。

let {toString:a} = 123;
a === Number.prototype.toString // true

let {toString:a} = 123;
a === Boolean.prototype.toString // true
複製代碼

解構賦值的規則是,若等號右邊的值不是對象或者數組,就會先將其轉化成對象。因爲undefinednull沒法轉化成對象,因此對其進行解構賦值時會報錯。

let { a:b } = undefined;
//TypeError: Cannot destructure property `a` of 'undefined' or 'null'

let { a:b } = null;
//TypeError: Cannot destructure property `a` of 'undefined' or 'null'
複製代碼

函數參數的解構賦值

function add({x,y]){
    return x+y;
}
add({1,2}); //3
複製代碼

函數add的參數表面上爲一個數組,可是在傳入參數的那一刻,數組參數就被解構成了2個變量,xy

函數參數的解構也能夠用默認值。

function move({x=0,y=0} = {} ) {
    return [x,y];
}
move({x:1}); // [1,0];
move({}); // [0,0];
複製代碼

函數參數的解構有另外一種寫法,會得出另外一種結果。

function move({x,y} = {x:0,y:0} ) {
    return [x,y];
}
move({x:1}); // [1,undefined];
move({}); // [undefined,undefined];
move(); // [0,0]
複製代碼

上述的代碼時爲move函數參數設置默認值,而不是爲解構後的xy設置默認值,因此會得出不同的結果。

圓括號

在使用解構賦值的時候,圓括號是否使用,是一個問題。

ES6的規則中說明,只要有可能致使解構歧義的,就不能使用圓括號。

但因爲該規則的標準不容易衡量和辨別,因此通常是儘可能不使用圓括號。

不能使用圓括號的狀況

  1. 變量聲明語句

    let [(a)] = [1];
    let {x: (c)} = {};
    //上述兩句代碼顯示爲undefined
    
    let ({x: c}) = {};
    let {(x: c)} = {};
    let {(x): c} = {};
    let { o: ({ p: p }) } = { o: { p: 2 } };
    //上述四句代碼會報錯。
    複製代碼

    上述代碼發生這種狀況,主要是由於它們都是變量聲明語句,模式不能使用圓括號。

  2. 函數參數 函數參數也屬於變量聲明,所以不能帶圓括號。

    function a( [ ( b ) ] ) { return c; }
    複製代碼
  3. 賦值語句的模式

    ( { a:b } ) = { a:1 };
    
    [ ({a:b}) , { c:d } ] = [{},{}];
    複製代碼

    不管是將整個模式放入圓括號中,仍是將部分模式放入圓括號中,都會致使報錯。

解構賦值的用途

  1. 交換變量的值

    let a = 1;
    let b = 2;
    [a,b] = [b,a]
    複製代碼
  2. 從函數返回多個值

    經過解構賦值,能夠很方便的從數組或者對象裏獲取多個返回值。

    function arr(){
        return [1,2,3];
    }
    let [a,b,c] = arr();
    //返回一個數組
    
    
    function arr() {
        return {
            a:1,
            b:2
        };
    }
    let { a,b } = arr();
    複製代碼
  3. 函數參數的定義 解構賦值能夠方便地將一組參數與變量名對應起來。

    // 參數是一組有次序的值
    function f([a, b, c]) { ... }f([1, 2, 3]);
    
    // 參數是一組無次序的值
    function f({a, b, c}) { ... }f({z: 3, y: 2, x: 1});
    複製代碼
  4. 提取JSON數據 在提取JSON對象中的數據時,解構賦值能起到很是簡便和快速的做用,使得代碼更加簡潔。

    let jsonData = {
      id: 42,
      status: "OK",
      data: [867, 5309]};
    
    let { id, status, data: number } = jsonData;
    
    console.log(id, status, number);
    複製代碼
  5. 函數參數的默認值 經過使用解構賦值,在給函數參數賦予默認值時,整個代碼會顯得更加簡潔。

    jQuery.ajax = function (url, {
      async = true,
      beforeSend = function () {},
      cache = true,
      complete = function () {},
      crossDomain = false,
      global = true,
      // 在這裏設置默認值
    } = {}) {
      // 這裏則是賦值的內容,若爲undefined,則使用默認值
    };
    複製代碼
  6. 遍歷Map結構 上文說過,面對擁有Iterator接口的對象時,可使用解構賦值。在這裏,咱們能夠經過解構賦值快速的獲取鍵名和鍵值。

    const map = new Map();
    map.set('first', 'hello');
    map.set('second', 'world');
    
    for (let [key, value] of map) {
      console.log(key + " is " + value);}
    // first is hello
    // second is world
    複製代碼
  7. 輸入模塊的指定方法 加載模塊時,須要指定輸入哪些方法。解構賦值使得輸入語句很是清晰。

    const { SourceMapConsumer, SourceNode } = require("source-map");
    複製代碼

總結

在使用解構賦值的時候,總體感受上其實就是一個遍歷過程的簡化。我的感受最大的做用是能夠將相似邏輯的代碼進行過程簡化,從而給代碼瘦身。

同時在其中也發現了原文章中的部分細節錯誤。例如不能使用圓括號的狀況中的第一點,示例代碼中的前兩行代碼並無報錯,而是顯示undefined

而後這裏給本身留一個小做業,是在和朋友聊上述細節錯誤時發現的一個問題:爲何let [(a)] = [1];顯示undefined,而用[(a)] = [1]則會顯示[1]

參考文章

ECMAScript 6 入門:變量的解構賦值

相關文章
相關標籤/搜索