ES6:变量解构赋值

解构赋值是对赋值运算符的扩展,是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。

数组的解构赋值(Array)

基本

let [a,b,c] = [1,2,3];
console.log('a = '+a);  // 1
console.log('b = '+b);  // 2
console.log('c = '+c);  // 3

可嵌套

let  [foo, [[bar],baz]] = [1,[[2],3]];
console.log('foo = '+foo);  // 1
console.log('bar = '+bar);  // 2
console.log('baz = '+baz);  // 3

可忽略

let [,,third] = ["foo","bar","baz"];
console.log('third = '+third);  // baz

let [x, , y] = [1, 2, 3];
console.log('x = '+x);  // 1
console.log('y = '+y);  // 3

剩余运算符

let [head, ...tail] = [1,2,3,4];
console.log(head);  // 1
console.log(tail);  // [2,3,4]

let [x,y,...z] = ['a'];
console.log(x);  // 'a'
console.log(y);  // undefined
console.log(z);  // []
// 如解构不成功,变量的值为undefined

如解构不成功,变量的值为undefined

let [ff] = [];
console.log(ff);    // undefined
let [m,n] = [];
console.log(m,n);   // undefined undefined

let bb = [];
bb = [99,88];
let [mm,nn] = bb;
console.log(mm,nn);   // 99 88

不完全解构

等号左边的模式只匹配一部分的等号右边的数组,解构依然成功。

let [aaa = 1, bbb] = []; 
console.log(aaa,bbb);   // 1 undefined

let [aaaa, bbbb] = [1,2,3]; 
console.log(aaaa,bbbb);   // 1 2

let [xx, [yy] ,zz] = [1,[2,3],4]; 
console.log(xx,yy,zz);   // 1 2 4

如果等号的右边不是可遍历的结构,那么将会报错。

// let [k] = 1;  // Uncaught TypeError: 1 is not iterable
// let [x] = false;  
// let [h] = NaN;  
// let [h] = undefined;  
// let [h] = null;  
// let [h] = { };  

上边代码中的语句都会报错,因为等号右边的值或是转为对象后不具备Iterator接口(前5个表达式),或是本身不具备Iterator接口(最后表达式)。

只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。

let [e,f,g] = new Set(['a','b','c']);
console.log(e);  // a

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();
console.log(sixth);  // 5

解构默认值

let [ee = true] = [];
console.log('ee = '+ee);   // ee = true

let [xxx,yyy='b'] = ['a'];
console.log('xxx = '+xxx); // xxx = a
console.log('yyy = '+yyy); // yyy = b

let [x1,y1='b'] = ['a',undefined];
console.log('x1 = '+x1);  // x1 = a
console.log('y1 = '+y1);  // y1 = b

ES6内部使用严格相等运算符(===)判断一个位置是否有值。所以,如一个数组成员不严格等于undefined,默认值是不生效的。

let [x2 = 1] = [undefined];
console.log('x2 = '+x2);  // x2 = 1

let [x3 = 1] = [null];
console.log('x3 = '+x3);  // x3 = null

如默认值是一个表达式,那该表达式是惰性求值的,即只有用到时才会求值。

function f1(){
    console.log('kkkxxxhhh');
}
let [x4 = f1()] = [1];
console.log('x4 = '+x4); // x4 = 1

// 等价于下面代码
// let x4;
// if([1][0] === undefined){
//     x4 = f1();
// }else{
//     x4 = [1][0];
// }

默认值可引用解构赋值的其他变量,但该变量须已声明。

let [m1=1,n1=m1] = [];
console.log('m1 = '+m1+',n1 = '+n1);
// m1 = 1,n1 = 1

let [m2=1,n2=m2] = [2];
console.log('m2 = '+m2+',n2 = '+n2);
// m2 = 2,n2 = 2

let [m3=1,n3=m3] = [1,2];
console.log('m3 = '+m3+',n3 = '+n3);
// m3 = 1,n3 = 2

// let [m4=n4,n4=1] = [];
// console.log('m4 = '+m4+',n4 = '+n4);
// Uncaught ReferenceError: Cannot access 'n4' before initialization

对象模型的解构(Object)

对象解构与数组解构不同:

数组元素是按次序排列的,变量取值是由它的位置决定的;

而对象属性没有次序,变量必须与属性同名才能取到正确的值。

基本

let { k1, h1 } = { k1: 'aaa', h1: 'bbb' };
console.log('k1 = '+k1+',h1 = '+h1);
// k1 = aaa,h1 = bbb
 
let { baz : k } = { baz : 'ddd' };
console.log('k = '+k);
// k = ddd

let { k2, h2 } = { h2: 'bbb' , k2: 'aaa',};
console.log('k2 = '+k2+',h2 = '+h2);
// k2 = aaa,h2 = bbb

let { max } = { foo:'aaa',bar:'bbb', };
console.log('max = '+max);
// max = undefined
// 变量没有对应的同名属性,则取不到值,为undefined

如变量名与属性名不一致,必须写成如下样子:

var {k3:ba} = {k3:'aaa',h3:'bbb',};
console.log('ba = '+ba);
// ba = aaa

let obj = {first:'hello',last:'world'};
let {first:f0,last:l0,} = obj;
console.log('f0 = '+f0+',l0 = '+l0);
// f0 = hello,l0 = world

对象的解构赋值是下面形式的简写:

let {first:first,last:last,} = {first:'hello',last:'world'};

对象解构赋值内部机制是先找到同名属性,后再赋值给对应的变量。

真正赋值的是后者,而不是前者。

let {k4:bb0,} = {k4:'aaa',h4:'bbb'};
console.log('bb0 = '+bb0); 
// bb0 = aaa
console.log('k4 = '+k4);
// Uncaught ReferenceError: k4 is not defined

可嵌套可忽略

let obj0 = {
    p:[
        'Hello',
        {t:'World'}
    ]
};
let {p:[s,{t}]} = obj0;
console.log('s = '+s+',t = '+t);
// s = Hello,t = World

上面代码段,p是模式,不是变量,不会被赋值。若要p作为变量赋值,如下:

let obj0 = {
    p:[
        'Hello',
        {t:'World'}
    ]
};
let {p,p:[s,{t}]} = obj0;
console.log(p);
// ["Hello", {t:'World'}]
console.log('s = '+s+',t = '+t);
// s = Hello,t = World
let node = {
    loc:{
        start:{
            line:1,
            column:5,
        }
    }
};
var { loc, loc:{ start }, loc:{ start:{line}, }, } = node;
console.log(line);
// 1
console.log(loc);
// { start: {line:1,column:5,} }
console.log(start);
// {line: 1, column: 5}
let obj1 = {};
let arr1 = [];
( { foo1:obj1.prop,bar1:arr1[0] } = { foo1:"dengjing",bar1:true, } );
console.log(obj1,arr1);
// {prop: "dengjing"} [true]

解构默认值

var {k5 = 3} = {};
console.log('k5 = '+k5);
// k5 = 3

var {k6,h6 = 5} = { k6 : 1 };
console.log('k6 = '+k6+',h6 = '+h6);
// k6 = 1,h6 = 5

var {k7: h7 = 3} = {};
console.log('h7 = '+h7);
// h7 = 3

var {k7: h7 = 3} = {k7:5};
console.log('h7 = '+h7);
// h7 = 5

var { message:msg = `Something went wrong!` } = {};
console.log('msg = '+msg);
// msg = Something went wrong!

默认值生效的条件是:对象的属性值严格等于undefined。

var {d = 3} = {x:undefined};
console.log('d = '+d);
// d = 3

var {f2 = 3} = {x:null};
console.log('f2 = '+f2);
// f2 = 3

如解构失败,变量值为undefined。

let {f3} = {bar:'kkxfh'};
console.log('f3 = '+f3);
// f3 = undefined

如解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错。

// let {foo: {r}} = {baz :`baz` } ;
// Uncaught TypeError: Cannot destructure property `r` of 'undefined' or 'null'.

下段代码报错:syntax error

let zzz0 ;
{zzz0} = {zzz0:1};

上代码会报错,因js引擎会将{x}理解成一个代码块,从而发生语法错误。

只有不将大括号写在行首,避免js将其解释为代码块,才能解决bug。

let zzz0 ;
({zzz0} = {zzz0:1});
console.log('zzz0 = '+zzz0);
// zzz0 = 1

对象的解构赋值可很方便地将现有对象的方法赋值到某个变量。

let {log,sin,cos} = Math;

数组本质是特殊的对象,因此可对数组进行对象属性的解构。

let arr = [1,2,3];
let {0:first0,[arr.length-1]:last0} = arr;
console.log('first0 = '+first0,'last0 = '+last0,);
// first0 = 1 last0 = 3

剩余运算符

let {a9, b9, ...rest} = {a9: 10, b9: 20, c: 30, d: 40};
console.log('a9 = '+a9,'b9 = '+b9,);
// a9 = 10 b9 = 20
console.log(rest);
// {c: 30, d: 40}

字符串的解构(String)

在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。

可遍历对象即实现 Iterator 接口的数据。

let [a09, b09, c09, d09, e09] = 'hello';
console.log('a09 = '+a09,);
// a09 = h
console.log('b09 = '+b09,);
// b09 = e
console.log('c09 = '+c09,);
// c09 = l
console.log('d09 = '+d09,);
// d09 = l
console.log('e09 = '+e09,);
// e09 = o

类似数组的对象都有一个length属性,因此可对这个属性进行解构赋值。

let {length:len} = 'hello';
console.log('len = '+len);
// len = 5

数值和布尔值的解构赋值

解构赋值时,如等号右边是数值和布尔值,则会先转为对象。

let {toString:s0} = 123;
console.log( s0 === Number . prototype.toString );
// true
console.log(s0);
// ƒ toString() { [native code] }


let {toString:s1} = true;
console.log( s1 === Boolean.prototype.toString );
// true
console.log(s1);
// ƒ toString() { [native code] }

上面代码段中,数值和布尔值的包装对象都有toString属性,so变量都能取到值。

解构赋值的规则是:只要等号右边的值不是对象或数组,就先将其转为对象。

let { prop:prop01 } = undefined;
// Uncaught TypeError: Cannot destructure property `prop` of 'undefined' or 'null'.
let { prop:prop02 } = null;
// Uncaught TypeError: Cannot destructure property `prop` of 'undefined' or 'null'.

上面代码段报错,undefined和null无法转为对象,so对其进行解构赋值时都会报错。

函数参数的解构赋值

function add([x,y]){
    return x+y;
}
console.log( add([1,2]) );    
// 3

函数add参数表面上是一个数组,但在传入参数那刻,数组参数就被解构成变量x和y。对于函数内部的代码来说,它们能感受到的参数就是x和y。

let arr2 = [ [1,2],[3,4] ].map( ([a,b])=>a+b );
console.log(arr2);
// [3, 7]

函数参数的解构也可以使用默认值。

// 为变量 x、y 指定默认值
function move({x=0,y=0}={}){
    return [x,y];
}
console.log( move({x:3,y:8}) );
// [3, 8]
console.log( move({x:3,}) );
// [3, 0]
console.log( move({}) );
// [0, 0]
console.log( move() );
// [0, 0]
// 为函数move1的参数指定默认值
function move1({x,y}={x:0,y:0}){
    return [x,y];
}
console.log( move1({x:3,y:8}) );
// [3, 8]
console.log( move1({x:3,}) );
// [3, undefined]
console.log( move1({}) );
// [undefined, undefined]
console.log( move1() );
// [0, 0]

undefined就会触发函数参数的默认值

let arr3 = [1 , undefined, 3].map( (x = 'yes' ) => x);
console.log( arr3 );
//  [1, "yes", 3]

用途

交换变量值

let oo=1,pp=2;
[oo,pp] = [pp,oo];
console.log( 'oo = '+oo );
// oo = 2
console.log( 'pp = '+pp );
// pp = 1

从函数返回多个值

函数只能返回一个值,如要返回多个值,只能将它们放在数组或对象里返回。

// 返回一个数组
function example(){
    return [1,2,3];
}
let [x5,h5,r5] = example();
console.log(x5,h5,r5);
// 1 2 3

// 返回一个对象
function example1(){
    return {
        x8:1,
        h8:2,
        r8:3,
    };
}
let {x8,h8,r8,} = example1();
console.log(x8,h8,r8,);
// 1 2 3

函数参数的定义

// 参数是一组有次序的值
function ffff([x,y,z]){
    // ...
}
ffff([1,2,3])

// 参数是一组无次序的值
function ffff1({x,y,z}){
    // ...
}
ffff1({z:3,y:2,x:1})

提取JSON数据

解构赋值对提取JSON对象中的数据很好用

let jsonData = {
    id:42,
    status:'ok',
    data:[867,5309],
};
let {id,status,data:number} = jsonData;
console.log(id,status,number);
// 42 "ok" [867, 5309]

函数参数的默认值

指定参数的默认值,如此就避免了在函数体内部再写var foo = config.foo||'default.foo';这样的语句。

jQuery.ajax = function(url,{
    async = true,
    beforeSend = function(){},
    cache = true,
    complete = function(){},
    crossDomain = false,
    global = true,
    // ... more config
}){
    // ... do stuff
}

遍历Map结构

任何部署了Iterator接口的对象都可以用for...of...循环遍历。

Map结构原生支持Iterator接口,配合变量的解构赋值获取键名和键值就非常方便了。

var map = new Map();
map.set('fst','hello');
map.set('snd','world');
for(let [key,value] of map){
    console.log(key+' is '+value);
}
// fst is hello
// snd is world

如只想获取键名或键值,可写成如下样:

// 获取键名
for(let [key] of map){
    // ...
}
// 获取键值
for(let [,value] of map){
    // ...
}

输入模块的指定方法

加载模块时,往往需要指定输入的方法。解构赋值使得输入语句非常清晰。

const { SourceMapConsumer, SourceNode } = require ('source-map');
console.log(SourceMapConsumer);
console.log(SourceNode);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值