变量的解构赋值

920 阅读5分钟

不同类型解构赋值的特点
1、解构赋值顺序: 按位解构or按名解构
2、可以赋默认初始值

一、数组的解构赋值

特点:
1、按位赋值(即按顺序解构)
2、可以赋默认初始值

作用
1、交换变量的值
2、给变量赋默认初始值
3、在函数中给参数赋默认初始值

// 解构赋值
var [a, b, c] = [1, 2, 3];
console.log(a, b, c);//1 2 3
// 等同于👇
var a = 1, b = 2, c = 3;

// 作用1 => 交换变量的值
var a = 3;
var b = 4;
[a, b] = [b, a];   //交换a,b的值
console.log(a, b); //4 3
// 作用2 => 给变量赋默认初始值   
let [a, b    ] = [10];    //10 undefined
let [a, b = 3] = [10];    //10 3
let [a, b = 3] = [10, 20];//10 20 
//既有赋值又有初始值,赋值操作优先于初始值
// 作用3 => 在函数中给参数赋默认初始值
// 但是此方法一般不用,因为ES6可以直接给参数赋默认值
function fn([a, b, c = 10]) {
    console.log(a + b + c);
}
fn([1, 2]); //12

// 补充:ES6参数默认初始值
function fn(a, b, c = 2) {
    console.log(a + b + c);
}
fn(3, 5); //10

二、对象的解构赋值

特点与应用:
1、对象解构赋值是按照属性名解构(不是按顺序解构)
2、可以设置默认初始值
3、对象解构过程中,如果有重名属性,值被覆盖(需要使用冒号: 赋予一个新的变量名)
4、在函数传参中的应用
5、不能使用对象解构赋值的场景: 函数中使用了this
6、隐性的对象解构赋值

1、按属性名解构
var { a, b, c } = { a: 1, b: 2, c: 3 };
var { a, b, c } = { b: 2, a: 1, c: 3 };//无关顺序 与上面的写法等效
console.log(a,b,c); //1 2 3

2、设置默认初始值   
var { a, b = 10, c } = { a: 1, c: 3 }
console.log(a, b, c);//1 10 3
3、对象结构属性名重复的问题
var { a, b: { a } } = { a: 1, b: { a: 2 } }; 
console.log(a);// 2 解析: a属性重名了(解构时a先解构为1,再解构为2,第二次赋值的2时覆盖了第一次赋值的1,所以最终结果为2
console.log(b);// b is not defined 解析: b已经被解构了,因此不存在变量b了

解决方法: a、杜绝重名 b、使用冒号: 赋予一个新的变量名,避免结构出来后,值被覆盖
// 补充:只有对象解构有这种问题,也只有对象结构需要使用这种方式

var { a, b: { a: a1 } } = { a: 1, b: { a: 2 } }; 
console.log(a, a1);// 1 2
console.log(b);    // b被解构了,结果依旧是b is not defined
// 4、 对象解构赋值在函数中的应用
//     a. 函数参数赋默认初始值
//     b. 传参时,解构赋值可以让参数传递顺序不同
function fn({ a, c, b = 10, d }) {
    console.log(a + b + c + d);
}

fn({ a: 1, c: 4, d: 5 });//20
5)不能使用对象解构赋值的场景: 函数中使用了this

var obj = {
    n: 1,
    a: function () {
        this.n++;
        console.log(this.n);
    },
    b: function () {
        console.log("b");
    },
    c: {
        d: function () {
            console.log("c");
        }
    }
}

// 非结构赋值时执行
obj.a();  //2
obj.b();  //b
obj.c.d();//c

// 解构赋值后执行
var { a, b, c: { d } } = obj;
a(); //NaN <-- 注意与非解构时执行的区别
b(); //b
d(); //c
// 解析:
// 解构赋值后执行对象的方法时,被解构的abd会变成全局方法
// 而将对象的方法解构为全局方法时,对象中的this会被重新指向全局的this,导致使用时可能出现混乱
// 故判断是否使用解构赋值的一个重要依据是: 函数中有没有this
6、隐性的对象解构赋值
字符串、数组等本质是对象,因此也可以通过解构赋值获取其属性和方法

// 1)字符串
// 1.1 解构获取字符串的 length 值
let { length } = 'abcde';
console.log(length);//5

// 1.2变形 将 length 赋值给变量len
let { length: len } = 'abcde';
console.log(length);//5

// 2)数组
// 2.1 解构数组中的slice等方法
let { slice, concat, push } = []; //数组本质是对象,有slice等方法
console.log(slice, concat, push);

// 2.2 把类数组转换成数组
// 常规写法
Array.prototype.slice.call(类数组);// 写法一
[].slice.call(类数组);// 写法二

// 解构赋值写法
let { slice } = [];
console.log(slice.call(类数组));

三、字符串的解构赋值

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象
因此字符串解构时,会先将字符串转换成对象,再进行解构赋值
字符串解构既有按位解构,也有按名解构

作用
1、解构字符串中的字符 (按顺序解构)
2、提取JSON字符串数据 (按名解构)
3、获取字符串的属性方法 (按名解构)

1、解构字符串中的字符(按顺序解构) 

// 例子1 解构获取x\y\z
let [a, b, c] = 'xyz';
console.log(a, b, c)

// 例子2 解构获取o和w
let [, , , , x, , y] = 'hello world';
console.log(x, y)
2、提取JSON字符串数据(按名解构) 

// 例子1  对象型字符串解构
var str = '{"a":1,"b":10}';
var { a, b } = JSON.parse(str); //{a: 1, b: 10}
console.log(a, b);

// 例子2  数组型字符串解构
var str = "[1,2,3]";
var [a, b, c] = JSON.parse(str);//[1, 2, 3]
console.log(a, b, c);
3、获取字符串的属性方法

// 例子 解构获取字符串的 length 值
let { length } = 'abcde';
console.log(length);//5

四、数值和布尔值的解构赋值

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象
1、因此字符串、布尔、数值 转换成对象才能解构
2、由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

案例

// 将 Hello和 World 解构出来
let obj = {
    p: [
        'Hello',
        { y: 'World' }
    ]
};
// 解题核心: 对象按属性名解构,数组按位解构
var { p: [x, { y }] } = obj;
console.log(x, y); //Hello World