解構賦值(Destructuring)

解構賦值(Destructuring)

ES6 允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構(Destructuring)。

數組的解構賦值

以前,爲變量賦值,只能直接指定值。

let a = 1;
let b = 2;
let c = 3;

ES6 允許寫成下面這樣。

//等號左邊是在數組中聲明多個變量
//等號右邊是一個數組
let [a, b, c] = [1, 2, 3];
console.log(a,b,c);//->1 2 3

上面的代碼表示從,可以從數組中提取值,按照位置賦值給左側的變量

默認值

解構賦值允許指定默認值。

//y在右側數組中對應位置沒有值,即undefined,會使用默認值
let [x,y=2]=[1] //x->1 y->2
//同上
let [x=1,y]=[,2] //x->1 y->2
//同上
let [x=1,y=2]=[] //x->1 y->2

注意,ES6 內部使用嚴格相等運算符(===),判斷一個位置是否有值。所以,只有當一個數組成員嚴格等於undefined,默認值纔會生效。

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

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

默認值可以是一個表達式,只不過這個表達式是惰性的,使用到的時候纔會執行求值。

function f(){ console.log("aaaaaa"); return 1; }
let [x=f()]=[];
//aaaaaa
//x->1

默認值可以引用解構賦值的其他變量,但該變量必須已經聲明。

let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined

上面最後一個表達式之所以會報錯,是因爲x用y做默認值時,y還沒有聲明。

注意事項

  1. 如果解構不成功,那麼變量的值將會是undefined
let [foo] = [];
let [bar, foo] = [1];

以上兩種情況foo的值都是undefined

  1. 數組的解構可以用於嵌套賦值
let [x,[y,z],l]=[1,[2,3],4]
//x->1,y->2,z->3,l->4
  1. 解構存在不完全解構,即等號左邊的模式,只匹配一部分等號右邊的數組。這種情況下依然可以解構成功。
let [x, y] = [1, 2, 3];
x // 1
y // 2

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

以上兩個例子都屬於不完全解構,但是夠可以成功。

  1. 如果等號右邊的不是數組,嚴格的說不是可以遍歷的結果,那麼將會報錯。
// 報錯
let [foo] = 1;          //VM415:1 Uncaught TypeError: 1 is not iterable
let [foo] = false;      //VM415:1 Uncaught TypeError: false is not iterable
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

以上代碼都會報錯。

  1. 只要某種數據結構具有 Iterator 接口,都可以採用數組形式的解構賦值
let [a,b,c]=new Set([1,2,3]);
//a->1,b->2,c->3

對象的解構賦值

解構賦值不僅可以用於數組,也可以用於對象。

//這裏的變量名與對象的屬性同名
let {foo,bar}={foo:"a",bar:"b"}
//foo->a bar->b

對象的解構與數組有一個重要的不同,數組中的元素是按照次序排列的,變量名的取值由他的位置決定;而對象沒的屬性沒有次序,變量名必須與屬性同名才能取到正確的值。即數組的結果是按照元素的次序給變量賦值,對象的解構是按照變量名和屬性名匹配做賦值。

如果要賦值的變量名和對象屬性名不一樣,可以使用匹配模式:變量名稱寫法。

//f、b爲匹配模式,會去匹配同名的屬性
//將匹配到的同名屬性的值賦給foo、baz
let {f:foo,b:baz}={f:"a",b:"b"}
//foo->a bar->b

其實第一個示例代碼的完整寫法是這樣的。

//這裏的變量名與對象的屬性同名
let {foo:foo,bar:bar}={foo:"a",bar:"b"}
//foo->a bar->b

默認值

對象的解構賦值也可以給默認值

let {foo='a',bar}={bar:'b'} //foo->a bar->b
let {foo='a',bar='b'}={}    //foo->a bar->b
let {foo='a',bar=foo}={}    //foo->a bar->a
var { message: msg = 'Something went wrong' } = {};
//msg->"Something went wrong"

使用默認值的條件是,對象屬性值嚴格等於(===)undefined

let {foo='a'}={foo:undefined}   //foo->a
let {foo='a'}={foo:null}        //foo->null

注意事項

  1. 對象解構失敗,默認值將會是undefined,這一點和數組解構賦值一致
let {foo,bar}={foo:"a"}
//foo->a bar->undefined
  1. 對象的解構也可以用於嵌套賦值,這一點和數組的解構嵌套賦值一致
let {foo,bar:{baz}}={foo:"a",bar:{baz:"b"}}
//foo->a,bar->b

下面是一個更爲複雜的例子

const node = {
  loc: {
    start: {
      line: 1,
      column: 5
    }
  }
};

let { loc, loc: { start }, loc: { start: { line }} } = node;
//loc->{start: {…}}
//start->{line: 1, column: 5}
//line->1

上面的代碼進行了三個解構賦值,分別是loc、start和line。這裏需要注意的是:loc解構賦值時,loc即時匹配模式又是變量;start解構賦值時,loc是匹配模式,start即使匹配模式又是變量;line解構賦值時,loc\start都是匹配模式,line即是匹配模式又是變量。

  1. 如果解構模式是嵌套的對象,而且子對象所在的父屬性不存在,那麼將會報錯。
// 報錯
let {foo: {bar}} = {baz: 'baz'};

foo模式需要匹配一個對象,但是foo並沒有匹配到一個對象,值將會是undefinedundefined.bar會報錯。

幾個用於鞏固知識點的代碼示例

  1. 解構一個對象,賦值給數組
function test(){
    let array=[];
    let obj={foo:'a',bar:'b'};
    ({foo:array[0],bar:array[1]}=obj);
    console.log("array:",array);//["a", "b"]
}
test();
  1. 解構一個數組,賦值給對象
function test1(){
    var obj={foo:[],bar:[]};
    var array = [[1,2],['a','b']];
    ([obj.foo,obj.bar]=array);
    console.log("obj:",obj);//{bar:["a", "b"],foo:[1, 2]}
}
test1()
  1. 對象和數組解構賦值嵌套使用
function test2(){
    var obj={userName:[{fistName:'Zhang'},{lastName:'Xueyuan'}]}
    var userNameObj={};
    var userNameAray=[];

    ({userName:[{fistName:userNameObj.fistName},{lastName:userNameObj.lastName}]}=obj);
        
    ({userName:[{fistName:userNameAray[0]},{lastName:userNameAray[1]}]}=obj);

    console.log("userNameObj:",userNameObj);
    //{fistName: "Zhang", lastName: "Xueyuan"}

    console.log("userNameAray:",userNameAray);
    //["Zhang", "Xueyuan"]
}
test2()

字符串的解構賦值

字符串也可以解構賦值,如果等號的右側是字符串,那麼該字符串會轉換成數組對象(Array)

let [a,b]="ab"; //a->a b->b
//上面的代碼相當於
let [a,b]=new Array('a','b');

字符串轉換數組對象後,也可以使用對象的解構方式解構數組對象的屬性

let {length:len}="ab";  //len->2

也就是說字符串的解構賦值,既可以使用對象的解構賦值,也可以使用數組的解構賦值。

數值和布爾值的解構賦值

數值和布爾值也可以解構賦值,如果等號的右側是數值或布爾值,那麼數值或布爾值將轉換正對應的包裝類,數值Number,布爾值Boolean。

let {valueOf:fun}=true;
let {valueOf:fun}=123;
//這裏是把valueOf函數賦值給了變量,並不是將valueOf函數的執行結果賦值給fun

注意:數值和布爾值不能使用數組解構賦值,應該兩者都不是可迭代(Iteration)的對象。

函數的參數解構賦值

解構賦值可以用於數組和對象,也可以用於函數的解構賦值。原理都是一樣的,一個函數需要一個或多個順序排列的形參,在調用是順序傳入實參,使用解構數組或對象的形式作爲形參,調用的時候傳入數組或對象作爲實參。

//普通的寫法
function add(x,y){
    return x+y;
}
add(1,2);//->3

//使用數組解構賦值的形式給函數傳遞參數
function add([x,y]){
    return x+y;
}
add([1,2]);//->3

//使用對象解構賦值的形式給函數傳遞參數
function add({x,y}){
    return x+y;
}
add({x:1,y:2});//->3

默認值

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

//函數參數數組解構賦值指定默認值
function add([x=0,y=0]=[]){
    return x+y;
}
console.log(add([1,2]));//->3   相當於[x=0,y=0]=[1,2]
console.log(add([1]));//->1     相當於[x=0,y=0]=[1]
console.log(add([]));//->0      相當於[x=0,y=0]=[]
console.log(add());//->0    //使用默認值[],相當於[x=0,y=0]=[]

//函數參數對象解構賦值指定默認值
function add({x=0,y=0}={}){
    return x+y;
}
console.log(add({x:1,y:2}));//->3    相當於{x=0,y=0}={x:1,y:2}
console.log(add({x:1}));//->1        相當於{x=0,y=0}={x:1}
console.log(add({}));//->0           相當於{x=0,y=0}={}
console.log(add());//->0    //使用默認值{},相當於{x=0,y=0}={}

注意:[x=0,y=0]=[]{x=0,y=0}={}等號左邊是解構賦值的變量,右側是調用函數不傳入任何參數時使用的默認值,而不是指定x、y變量的默認值。add()調用形式會使用默認值。

默認值非{}、[]的例子

function add([x=0,y=0]=[1,2]){
    return x+y;
}
console.log(add([1,2]));//->3
console.log(add([1]));//->1
console.log(add([]));//->0
console.log(add());//->3    //使用默認值{},相當於[x=0,y=0]=[1,2]

用途

  1. 交替變量的值
  2. 從函數返回多個值
  3. 函數參數的定義
  4. 提取JSON
  5. 函數參數的默認值
  6. 便利Map接口
  7. 輸入模塊的指定方法
function forMap(){
    let map=new Map();
    map.set("name","zhangxy");
    map.set("age",30);

    for(let [key,value] of map){
        console.log(key,":",value);
    }
}
forMap();
//單獨獲取key
for(let [key] of map){
        console.log(key,":",value);
}

//單獨獲取value
for(let [,value] of map){
        console.log(key,":",value);
}

總結起來解構賦值的語法讓變量的賦值更簡潔,用更少的代碼就能從數組或對象中獲取值賦給變量

學習資料:變量的解構賦值(https://es6.ruanyifeng.com/#docs/destructuring)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章