ECMAScript 6(4)变量的解构赋值

变量的解构赋值

解释:

1. 简单的来说,可以粗暴地理解为,等号左右两边的数组/结构类似时,将右边的值直接赋给左边对应位置的变量;
2. 主要面对的是数组和对象;
3. 虽说数值、字符串以及函数参数也能用,但核心思想是基于数组和对象的,理解好第二点,那么理解后面的也不难;

数组的解构赋值

【1】标准情况:

等号左右数组结构相同,左边的变量直接拿右边对应位置的变量的值。
如以下代码;

//基本版
var [a,b,c]= [1, 2, 3];
a;    //1
b;    //2
c;    //3

左边是一个长度为3的数组,右边也是一个长度为3的数组。
区别是左边是变量,右边是已知的值。
这样左右一一对应,变量a、b、c就分别被赋值了;

【2】左右结构相同,但有些位置没有对应的值;

如以下代码,原本b的位置没有变量了,这样的代码是可以跑通的。

//左缺失
var [a,,c]= [1, 2, 3];
a;    //1
c;    //3

又如以下代码,左边齐全,但是右边某个位置缺少变量。
这种情况下,无法对应的变量将被赋值undefined

//右缺失,解构不成功但不报错  
var [a,b,c]= [1, , 3];
a;    //1
b;    //undefined
c;    //3
【3】数组中嵌套数组

普通嵌套,一一对应的,此时对应位置对应值,理解起来简单暴力;

//数组嵌套  
var [a, [b, c], d]= [1, [2, 3], 4];
a;    //1
b;    //2
c;    //3
d;    //4

假如在以上基础上,左右位置有缺少,则如同【2】中缺少的情况一样,变量的值则为undefined,或者右边某个值没有找到左边对应的某个变量。

代码略

左边和右边非完全对应,如以下代码,则右边非对应的部分可以视为一个整体,或者左边解构失败报错

//左边对应右边某个元素  
var [a, b, c]= [1, [2, 3], 4];
a;    //1
b;    //[2,3],视为整体
c;    //4
//无法解构,抛出异常
var [a, [b, c], d]= [1, 2, 3];    //Uncaught TypeError: undefined is not a function
//扩展运算符... (三个句号,更多了解请参照以后的内容)   
var [a, ...b]= [1, 2, 3, 4];
b;    //[2, 3, 4]
【4】会报错的情况

具体来说,右边不是数组(严格的说,不是可遍历的结构),那么将会报错。

// 报错的情况 
let [foo] = 1;    //number
let [foo] = false;    //boolean
let [foo] = NaN;    //NaN
let [foo] = undefined;    //undefined
let [foo] = null;    //null
let [foo] = {};    //object

这时,我们回过头看【3】中抛出异常的那段代码,之所以报错,是因为实质上是[b, c] = 2,无法结构,所以报错。

【5】var、let、const都适用
【6】对于Set结构,也可以使用其来结构(在Set写完后补充);
【7】继续拓展,凡是具有Iterator接口的数据结构,都可以使用数组的形式进行结构赋值。

别问我什么是Iterator接口,请看后面专门讲Iterator的内容。


带默认值的数组解构赋值

解释:

1. 简单来说,就是在解构赋值时,给一个默认值;
2. 然后先看能不能解构成功,如果成功则取解构到的值,如果失败则取默认值;
【1】标准情况下:
var [a, b = 2, c, d = 10]=[1, , 3, 4];
a;    //1
b;    //2
c;    //3
d;    //4

在以上代码中,a和c和普通的解构赋值相同。

除此之外,左边多了一个b = 2和d = 10。于是,b的默认值是2,d的默认值是10,
而这两者的区别是右边对应解构位置,b的位置对应的是空内容,而d的位置对应的是4

因此,有默认值但解构失败的(准确的说是undefined)b,取了自己的默认值2;
有默认值但解构成功的的,则取了自己解构成功后的值4;

【2】右边不写和undefined是一样的
var [a = 1] = [undefined];  //1
var [a = 1] = [null];   //null
var [a = 1] = [3];  //3
var [a = 1] = ["abc"];  //"abc"
var [a = 1] = [{m: 1}]; //{m:1}
var [a = 1] = [true];   //true
var [a = 1] = [NaN];    //NaN
【3】在使用之前已经被声明的变量可以作为默认值使用。

简单来说,假如变量a先被声明,然后作为b解构时的默认值,那么是可以的;

var a = 1;
var [b = a] = [];
b;    //1

如果你不使用let、const等ES6新规定的声明模式,那么即使你先使用后声明也可以(变量提升),虽然没有什么用,并且不推荐

var [b = a] = [];
var a = 1;
b;    //undefined

另外,严格模式下,以上这种声明方式不会报错。


对象的解构赋值

解释:

1. 简单来说,对象解构是根据key给变量赋值该key的val,数组解构是根据顺序给对应位置的变量赋值;
【1】标准情况

最简单的如以下代码:

var {'abc': a} = {'abc': 1};
a;    //1

两边都是对象,都有一个key是abc,区别是左边是变量名a,右边的是值1

于是同key的被赋值了。

【2】变量名就是key

如以下代码,右边有key是abc,左边直接放一个和key相同的变量名,则该变量被赋值同名key的值

var {abc} = {'abc': 1};
abc;    //1

以上代码的实质是:

var {abc: abc} = {'abc': 1};

声明时,第一个abc是key,第二个abc是变量名

【3】解构失败,简单来说,就是该变量没有对应的key,取值undefined
var {abc} = {};
abc;    //undefined
【4】脑洞一下,【1】和【2】情况结合下是什么?

答案是都起作用

var {a: b, a} = {a: 1};
a;    //1
b;    //1

第一个a是key,于是变量b被取值;
第二个a是变量名,所以该变量a被取值1;

【5】对已有变量进行解构赋值

具体来说,之前我们都是在声明变量时进行结构赋值(注意有var),那么假如已经声明了一个变量,然后进行解构赋值呢?
答案是可以,但需要用圆括号将解构赋值那段代码括起来。
假如该行没有被括起来,那么就会报错;
原因在于,没有var且没有被括号括起来的话,{a}被认为是代码块,而一个对象显然是不能被赋值给一个代码块的

var a;
({a} = {a: 1});    //括号括起来,正常运行  
var a;
{a} = {a: 1};    //Uncaught SyntaxError: Unexpected token =

以下代码是可以正常运行的,相当于声明了2次变量a

var a;
var {a} = {a: 1};

使用let和const时,不能声明两次,否则会报错(见const和let相关内容),但var可以

let a;
let {a} = {a: 1};    //报错,因为let不能2次
var a;
var {a} = {a: 1};    //不会报错, 2次var是允许的

一句话总结:
已声明变量,在解构赋值时需要用括号括住整行代码。

【6】解构赋值时,赋值形式并非深度复制

具体来说,如果被赋值的变量,对应的是一个对象(或数组),那么解构赋值时,赋值形式是以按引用传递时赋值的。

如以下代码,变量a的属性b是一个对象,他被赋值给变量b;
然后修改变量b中的属性c时,变量a中的属性b的属性c的值也随之变化了;
说明这是按引用传递,就像在正常情况下,将一个对象赋值给另外一个变量一样。

var a = {
    b: {c: 1}
};
a.b.c;  //1
var {b} = a;
b.c;    //1

b.c = 2;
b.c;    //2
    a.b.c;  //2

一句话总结:
解构赋值时,赋值形式【不是】深度复制

【7】用解构赋值可以方便的取出其他对象中的某个方法或属性

如以下代码中,将对象computed的方法取出来使用,简单明了

var computed = {
    add: function (a, b) {
        return a + b;
    },
    minus: function (a, b) {
        return a - b;
    }
};
var {add, minus} = computed;
add(2, 3);
minus(2, 3);

或者某个对象中有一个属性是对象,我们只需要修改这一个对象而不用修改其他属性时,就可以采用这样的方法将属性赋值给一个变量,然后修改这个变量即可(利用对象被赋值时的按引用传递的性质)

【8】数组可以视为带key的对象来进行解构赋值

简单来说,假如有一个数组arr,那么arr[0]就是数组的第一个元素,arr[1]就是数组的第二个元素,0和1就是这个数组的key

如以下代码

var arr = [2, 4, 6, 8];
var {
        0:a,
        1:b,
        [arr.length - 1]:d,
        [arr.length - 2]:c
}= arr;
a;    //2
b;    //4
c;    //6
d;    //8

但相反情况下,是不可以的。

var obj = {
    0: 1,
    1: 2
}
var [a,b] = obj;    //Uncaught TypeError: undefined is not a function

一句话总结:
左边数组右边只能是数组;左边对象,右边可以是数组,或对象

【9】对象的默认值

简单来说,就是对象在解构赋值时也可以赋默认值,如果解构成功不是undefined,则取解构的值,否则取默认值

如代码:

    var {
            a: A = 10,
            b:B,
            C=20
    }={b: 1};
    A;    //10
    B;    //1
    C;    //20

字符串的解构赋值

解释:

1. 字符串可以被解构赋值,视为一个类似数组的对象
【1】右边可以当做一个数组看待

如以下代码的str,就类似[“H”, “e”, “l”, “l”, “o”, ” “, “w”, “o”, “r”, “l”, “d”, “!”]这样一个数组

var str = "Hello world!";
var [first,second] = str;
first;    //H
second;    //e
【2】但他终究还是一个字符串,可以用解构赋值的形式,取出他作为字符串的各种方法

比如在以下代码中,就取出了split这个方法,并且用apply来让另外一个字符串作为this而调用

var str = "Hello world!";
var {split:method} = str;
method.apply("abc", [""]);    //["a", "b", "c"]

但是仔细想想,这个似乎也没有什么意义。
字符串继承的是String的方法,如果需要字符串的方法,干嘛不直接从String中去获取呢?


其他基本类型的解构赋值

解释:

1. 尝试转为对象,如果能转则可以解构赋值,比如布尔类型和数值类型;
2. 不能转则不能解构赋值,比如null和undefined;
【1】数值和布尔的值不能被取出

因为不能被转为数组或对象,因此他的值不能被取出;

但是可以取出他的方法,准确的说,是Number和Boolean的方法,参照上面的字符串的【2】内容;

【2】null和undefined无法解构

准确的说,是会报错,因为他不能被转为对象。

var un = undefined;
var {abc} = un;    //Uncaught TypeError: Cannot match against 'undefined' or 'null'.

函数参数的解构赋值

解释:

1. 函数参数可以解构赋值,解构方法和上面相同。
2. 函数参数可以有默认值,有点类似c++函数参数声明时的默认值。
【1】函数参数的解构赋值

先看代码

function add({x, y}) {
    return x + y;
}
add({x: 1, y: 2});    //3

Babel是这么处理以上代码以适配es5的,即将参数变为一个变量,然后分别将该变量的x属性和y属性赋值给变量x和遍历y,然后拿去用。

function add(_ref) {
    var x = _ref.x,
        y = _ref.y;

    return x + y;
}
add({ x: 1, y: 2 });

类似的还有数组,道理是相同的,注意需要按格式写,比如function([a, b])和function(a, b)是完全不同的,前者可以被解构赋值,而后者不行

【2】函数参数的默认值

函数参数也可以有默认值,比如以下代码

function add({x=10, y=20}) {
    return x + y;
}
add({x: 1, y: 2}); //3
add({x: 1});    //21
add({});    //30

函数也可以这么声明,效果是一样的。

function add({x=10, y=20}={}) {
    return x + y;
}

另外,右边的{}可以写东西,至于写了有什么用,要参考下面的代码,应该是完整版了
左、右都有默认值的情况下,各种情况

function show({x=10, y, z=100}={y: 5, z: 50}) {
    return [x, y, z];
}
show({x: 1, y: 2, z: 3}); //[1, 2, 3] 取传参
show({x: 1, y: 2}); //[1, 2, 100] 先传参再左,无右
show({x: 1});    //[1, undefined, 100]  先传参再左,无右
show({y: 1});    //[10, 1, 100]   先传参再左,无右 
show({z: 1});    //[10, undefined, 1]   先传参再左,无右
show({});    //[10, undefined, 100]   先传参再左,无右 
show();  //[10, 5, 50] 无传参时,先右,剩下的取左

简单总结一下:
(1)有传参时,永远优先使用传的参数的值;
(2)有传参时,传的参数未传的属性,如果等号左边有默认值,则取等号左边的值;
(3)有传参时,忽略等号右边的默认值;
(4)无传参时,先取等号右边的默认值,剩下的取等号左边的默认值。

一句话总结:
传参大于非传参,无传参时,右边的当做传参


关于解构赋值时的圆括号

【1】简单的看了一下,能不使用圆括号就避免使用圆括号,因为是容易出错。

具体来说,禁止使用的情况:
(1)变量声明语句禁止;
(2)函数参数中,使用解构时禁止;
(3)先声明后解构赋值时,左边、或者右边,全部被圆括号括起来,禁止;(只有上面说允许的那个情况才可以,具体看上面)

允许的情况:
(1)赋值语句的非模式部分。

以上具体总结来说,就是有的代码,赋值时是正常可以跑的,但是前面加个var之类变成声明语句,就会报错。
具体参照阮一峰的博客吧,我就不细写了,个人建议还是如非必要,尽量避免使用圆括号;
禁止和允许使用圆括号的链接


变量解构赋值的用途

【1】交换变量的值

不适合交换对象的属性,会出错

//交换对象会出错,因为按引用传递
var obj = {
    x: 1,
    y: 3
};
console.log(obj);    //{x:1, y:3}
({
    x: obj.y,
    y: obj.x
} = obj);
console.log(obj);    //{x:1, y:1}
var x = 1;
var y = 2;
[x, y] = [y, x];
[x, y];  //2,1
【2】在函数返回多个值时方便取出

函数默认只能返回一个值,
虽然我们也可以通过返回数组或者对象,来变相达到返回多个值,但将对象取出使用时就比较麻烦。

通过解构赋值,我们可以轻松将返回值分别赋给多个变量

function test() {
    return [1, 2, 3];
}
var [a,b,c] = test();
[a,b,c];    //[1,2,3]
【3】函数参数的定义

比如说,我们需要函数的参数有a,b,c三个。但是他们顺序是不确定的,我们传参的时候就需要注意顺序,
即function test(a, b, c){},假如参数a,b,c的值分别是2,4,6,那么就应该这样调用test(2,4,6)

有了解构赋值,我们就可以这么写,无视顺序了:

function test({a, b, c}) {
    console.log([a, b, c]);
}
test({b: 4, c: 6, a: 2});    //[2,4,6]
【4】提取json的值

比如ajax拿到一个用户信息,他是一个对象,比如是变量result,我们正常来说需要通过比如result.id, result.name等来获取值。
但现在可以通过结构变量,直接赋给变量id和name就行。

【5】函数参数给默认值

假如我们函数参数需要默认值,以前一般是写在函数里,要判断有没有传参,现在可以直接写在函数声明时了。

【6】获取模块的指定方法

比如我们调用了一个模块,需要使用它的a,b方法,之前是先将这个模块赋值给变量m,然后通过m.a和m.b来调用,现在可以直接赋值给a和b变量(也可以赋值给其他名字的变量);

【7】遍历Map结构

可以拿key和val去做些你想做的事情了,写法更简单。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值