ES6 中變量的解構賦值

1. 數組的解構賦值

解構: ES6 中容許按照必定的模式從數組和對象中提取值,而後對變量進行賦值,這被稱爲解構(Destructuring).數組

1. 基本用法

本質上,這種寫法屬於「模式匹配」,只要等號兩邊的模式相同,左邊的變量就會被賦予相應的值。數據結構

  • 徹底解構
let [a, b, c] = [1, 2, 3];
console.log(a); // 輸出1
let [a, b] = [1];
console.log(b); // 解構不成功時返回undefined
  • 不徹底解構app

    等號左邊只匹配到等號右邊的一部分。函數

    let [a, [b], c] = [1, [2, 3], 4];
    console.log(b); // 輸出2,只匹配到右邊的右邊數組[2, 3]中的2
  • 不能解構狀況ui

    若是等號右邊不是可遍歷的解構,或者說等號右邊的值或是轉換爲對象之後也不具有 Iterator 接口,那麼就會解構失敗。prototype

    let [a] = 1;
    let [a] = false;
    let [a] = NaN;
    let [a] = undefined;
    let [a] = null;
    let [a] = {}

    總結:事實上,只要某種數據結構具備 Iterator 接口,均可以採用數組形式的解構賦值。code

2. 默認值

解構賦值容許指定默認值。對象

ES6內部使用嚴格相等運算符(===)判斷數組的某個位置是否有值。因此,若是一個數組成員不嚴格等於undefined,默認值是不會生效的。接口

let [a = 2] = [null];
// a = null
let [a = 2] = [];
// a = 2

若是默認值是一個表達式,那麼這個表達式是惰性求值的字符串

function f() {
    return 2;
}
let [a = f()] = [1];
// a = 1, f函數不會執行

let [a = f()] = [];
// a = 2, f函數會執行

默認值能夠引用解構賦值的其它變量,可是該變量必須已經聲明

let [x = 1, y = x] = [];
// x = 1, y = 1
let [x = y, y = 1] = [];
// ReferenceError,在使用變量y時還並無聲明

2. 對象的解構賦值

對象解構賦值的內部機制是先找到同名屬性,而後再賦值給對應的變量,真正被賦值的是後者而不是前者

舉例說明:

let {foo:bar} = {foo:"aaa"};
console.log(bar); // bar = "aaa"
console.log(foo); // ReferenceError, foo is not defined
console.log({foo:bar}.foo); // {foo:bar}.foo = "aaa"

上面代碼中 foo 是匹配的模式,經過 foo 匹配到對象 {foo:bar}foo 屬性的值( bar 變量),而後將值賦給 bar 變量,這樣對象 {foo:bar}foo 屬性就有值了,即 bar 的值 "aaa".

// 實際上
let {foo1, foo2} = {foo1:"aaa", foo2:"bbb"};
// 是下面語句的簡寫形式
let {foo1:a, foo2:b} = {foo1:"aaa", foo2:"bbb"};

對象的解構也能夠指定默認值,默認值生效的條件是,對象的屬性值嚴格等於undefined

let {x = 3} = {x: undefined};
// x = 3
// 上面的語句等價於
let {x:x = 3} = {x: undefined};
// x = 3
// {x:x = 3} = 3 

let {x = 3} = {x: null};
// x = null

若是解構模式是嵌套的對象,並且子對象所在的父屬性不存在,那麼將會報錯

let {foo: {bar}} = {bar: 'bar'};
// 報錯,由於foo = {bar} = undefined,{bar}對象中的bar屬性在解構時會報錯,由於{bar}是undefined,undefined是不能轉換爲對象的,對undefined取bar屬性會報錯。 
// 和下面的代碼原理同樣:
let obj = {bar: "bar"};
obj.foo.bar // 報錯,由於obj.foo = undefined,對undefined取屬性會報錯。
"bar".foo // undefined,不會報錯,由於字符串能夠轉換爲對象

在將已經聲明的變量進行解構賦值時,要注意解構賦值語句不能直接寫在行首

let x;
{x} = {x: 1}; // 報錯,這個地方不是很懂
// 書上的解釋是:JS引擎將{x}理解成一個代碼塊,從而發生語法錯誤。
// 若想避免這個錯誤,能夠這樣寫:
({x} = {x: 1});

3. 字符串的解構賦值

字符串能夠解構賦值是由於字符串能夠轉換成一個相似數組的包裝對象。

let [a, b, c, d, e] = "hello";
// a = 'h'  b = 'e'  c = 'l'  d = 'l'  e = 'o'

因爲相似數組的包裝對象有一個 length 屬性,所以在解構賦值時能夠利用這個屬性。

let {length: len} = 'hello';
// len = 5

4. 數值和布爾值的解構賦值

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

let {toString: s} = 123;
console.log(s === Number.prototype.toString) // true

數值 123 被轉換爲對象,數值對象中有 toString 方法,與 toString 匹配,變量 s 中存儲的是 toString 方法,該方法就是 Number 原型對象中的 toString 方法。

5. 函數參數的解構賦值

函數參數的解構也可使用默認值

function f({x = 0, y = 0} = {}) {
    return {x, y};
}
console.log(f({x: undefined, y: 1}));
// 輸出: {0, 1}

詳細過程:調用函數f後,{x = 0, y = 0}={x:undefined,y:1}

因爲x是undefined,故x使用默認值即x=0,因爲y:1故經過解構賦值後y=1.

以前的誤區:注意是解構賦值,並非對象之間的賦值。

再看下面這個例子:

function f({x, y} = {x: 0, y: 0}) {
    return {x, y};
}
console.log(f({x: undefined, y: 1}));
// 輸出: {undefined, 1}
// 調用函數後,{x,y}={x:udefined,y:1},解構賦值後x=undefined,y=1
console.log(f({x: 1, y: 1}));
// 輸出: {1, 1}
// 調用函數後,{x,y}={x:1,y:1},解構賦值後x=1,y=1
console.log(f({}));
// 輸出: {undefined, undefined}
// 調用函數後,{x,y}={}={undefined,undefined},解構賦值後x=undefined,y=undefined
console.log(f());
// 輸出: {0, 0}  當不傳遞實參時,形參的值是原有的值
// 調用函數後,{x,y}={x:0,y:0},解構賦值後x=0,y=0

再次重申一下 {x,y}={x:1,y:2} 這種形式是解構賦值,千萬不要理解成對象之間的賦值!

6. 圓括號問題

對於編譯器而言,一個式子究竟是模式仍是表達式,沒有辦法一開始就知道,必須解析到(或解析不到)等號才能知道

  • 不能使用圓括號的狀況

    1. 變量聲明語句
    2. 函數參數
    3. 賦值語句表達式
  • 可使用圓括號的狀況

    賦值語句中的非模式部分可使用圓括號。

    [(a)] = [3]; // a並非模式
    ({ p: (d) } = {}); // p是模式,d並非模式

建議:不管什麼狀況都儘可能不要在模式中使用圓括號

7. 解構賦值的做用

  1. 交換變量的值

    let a = 1;
    let b = 2;
    [a, b] = [b, a];
  2. 方便處理函數返回值

    function f([x, y]) {
        return [x+1, y+2];
    }
    let [a, b] = f([1, 1]);
  3. 函數參數定義

    解構賦值能夠方便地將一組參數與變量名對應起來。

    function f1({x, y, z}){
        return x+y+z;
    }
    f1({y: 1, z: 2, x: 3 }); // 能夠作到實參沒有次序
  4. 提取JSON數據

    能夠很方便地從JSON中提取須要的數據。

    let JSON = {
        name: "happyCoding1024",
        age: 18,
        hobby: "coding"
    }
    let [name, age, hobby] = JSON;
  5. 函數參數默認值

    很是簡化方便地使用函數參數默認值。

    function f([x=0,y=0] = []){
        return x+y;
    }
    f([]); // 當傳入的是undefined時,就會使用默認值
  6. 遍歷Map結構

    任何部署了 Iterator 接口的對象均可以用 for...of 循環遍歷。Map 結構原生支持 Iterator 接口,配合變量的解構賦值獲取鍵名和鍵值很是方便。

    let map = new Map();
    map.set('first', 'hello');
    map.set('second', 'world');
    for (let [key, value] = map){
        console.log(key + "is" + value);
    }
  7. 輸入模塊的指定方法

    加載模塊時,每每須要制定輸入的方法,解構賦值使得輸入語句很是清晰。

    const {SourceMapConsumer, SourceNode} = require("source-map");
相關文章
相關標籤/搜索