一、什么是解构赋值
ES6中,通过解构赋值,可以将属性 / 值从对象 / 数组中取出,赋值给其他变量的操作就是解构赋值。
在下图上可以发现左侧和右侧都必须是数组,左侧可以定义 n 个变量,那么与之对应的右侧也需要有相应个数的变量值,我们左侧的变量从右侧取值的操作就是解构赋值。
二、数组解构
在上面的图示里可以看出来,数组解构就是从数组中提取值,只要等号两边的模式相同,左边的变量就会在右侧的数组里查找对应位置的值进行赋值操作。
数组结构的应用主要有:
-
对一维数组解构
-
对多维数组解构
-
对字符串解构
-
对...扩展运算符解构
1. 一维数组解构
传统的变量赋值是这样的:
let arr = [1,2,3];//把数组的值分别赋给下面的变量;
let a = arr[0];
let b = arr[1];
let c = arr[2];
console.log(a); // a的值为1
console.log(b); // b的值为2
console.log(c); // c的值为3
变量的解构赋值:
let [a,b,c] = [1,2,3]; //把数组的值分别赋给下面的变量;
console.log(a); // a的值为1
console.log(b); // b的值为2
console.log(c); // c的值为3
左侧是数组,右侧也是数组。
左侧的变量会根据索引,依次获取右侧数组里的值。
代码短了很多,可读性也很强,这种叫做数组的解构赋值。
一句话:按索引取值~!
-
不完全解构
当左边的模式(你可以理解为格式)与右边不完全一样的时候,那么赋值过程中,只会给模式匹配成功的部分的变量赋值。
-
没得到的值的变量,返回的就是 undefined。相当于只声明了变量,但是没赋值。
-
没有变量存储的值,就会被浪费掉。
-
let [a] = []; // a = undefined
let [name,age,add] = ["Lily", , "Chongqing"];
console.info( name ); // Lily
console.info( age ); // undefined
console.info( add ); // Chongqing
let [x, , z] = [1, 2, 3]; //x=1,z=3
// 传统写法
let arr = [1, 2, 3];
let x = arr[0];
let z = arr[2];
- 左侧变量可以设定默认值
如果变量的值取到了 undefined ,那么左侧变量返回的就是默认值。否则,就是右侧设定的值。
let [name="Lily",age=18] = [ "Mouse", 20 ] ; // age = 20
let [name="Lily",age=18] = [ "Mouse", 0 ] ; // age = 0
let [name="Lily",age=18] = [ "Mouse", NaN ] ; // age = NaN
let [name="Lily",age=18] = [ "Mouse", null ] ; // age = null
let [name="Lily",age=18] = [ "Mouse" ] ; // age = 18
let [name="Lily",age=18] = [ "Mouse" , undefined ] ; // age = 18
- 可以用来交换变量数据
let a = 1;
let b = 2;
[a,b] = [b,a];
console.info(a,b); // 2 1
2. 多维数组结构
let arr1 = [1,[2,3],4];
let [a1, [b1,c1,d1], e1] = arr1;
console.info( a1,b1,c1,d1,e1); // 1,2,3,undefined,4
按照索引对应就行。
如果两侧不一致:
let [a,b,c] = [1,[2,3]];
console.log(a,b,c); // 1 [2, 3] undefined
从这里可以看出,结构赋值其实就是一种模式匹配,两边模式要一致才行。
3. 字符串解构
字符串也可以这么玩,因为字符串的字符也是有索引的。在解构赋值的过程中,字符串被转换成了一个类似数组的对象。
// 证明字符串有索引
let str = "hello";
console.info( str[0] ); // h
console.info( str[1] ); // e
// 字符串解构
let str = "hello";
let [a,b, , ,c] = str ;
console.info( a,b,c); // h e o
4. 拓展运算符(...)
也称为扩展运算符 ,就是三个连续的点(...)。
作用:取出参数对象中可遍历的属性,拷贝到当前对象之中。(百度出来的)
我的个人理解是:可以把数组转为数据序列。
-
函数参数
function add(x, y) { // 参数 x,y 是个数据序列,逗号隔开。
return x + y;
}
let numbers = [4, 10]; // 数组
add(...numbers); // 14。 把数组转为数据序列,可以作为函数的参数。
- 复制数组
// 复制数组
let arr1 = [1, 2];
let arr2 = arr1; // 数组是引用数据类型。arr1,arr2 都指向同一个数组空间。
// 一个数据改了,另一个数据也改了
arr2[0] = 2;
console.info(arr1) // [2, 2] ,复制失败
// 用 ... 拓展运算符就可以复制数组
let arr1 = [1, 2];
let arr2 = [...arr1];
arr2[0] = 2 ;
console.info( arr1 ); // [1, 2] ,复制成功。arr1 不受 arr2 数据变化影响。
- 数组合并
// 传统做法
let arr = [1,2,3];
let newarr = [4, 5].concat(arr); // 用concat连接两个数组,返回一个新的数组副本
// ES6 拓展运算符
let arr = [1,2,3];
let newArr1 = [4, 5,...arr];
let newArr2 = [4, ...arr , 5];
console.info( newArr1 ); // [4, 5, 1, 2, 3]
console.info( newArr2 ); // [4, 1, 2, 3, 5]
- 与数组结构一起用
用于生成数组
let [first, ...rest] = [1, 2, 3, 4, 5];
console.info( first ); // 1
console.info( rest ); // [2, 3, 4, 5]
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
let [...rest, last] = [1, 2, 3, 4, 5];
// 报错
let [first, ...rest, last] = [1, 2, 3, 4, 5];
// 报错
- 将字符串转为真正的数组
let str = "hello";
let arr = [...str];
console.info( arr ); // ["h", "e", "l", "l", "o"]
三、对象解构
按照属性来进行解构赋值,如果解构不成功,变量的值等于 undefined。对象解构可以指定默认值。
1. 和数组解构的对比
-
数组的元素是按次序排列的,变量的取值由它的位置决定;
-
模式匹配,两边都必须是 Object 对象。
-
而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
// 对象的解构赋值,跟顺序没有关系
let {age, name } = { name:"Tom", age:18 };
console.info(age,name); // 18 , Tom
let { age:b, name:a } = { nam e:"Tom", age:18 };
console.info( b, a ); // 18 , Tom
左右属性名不一致,会得到 undefined。
let {age, nameZ } = { name:"Tom", age:18 };
console.info(age,nameZ); // 18 , undefined
let { age:b, nameZ:a } = { name:"Tom", age:18 };
console.info( b, a ); // 18 , undefined
对象解构语句不是以变量声明关键字(如var,let或const)开头,则需要将语句包裹在括号中。 否则会报错。
({ a, b } = { a:100, b:200 } ); // good
let { a, b } = { a:100, b:200 } ; // good
let a , b ;
{ a, b } = { a:100, b:200 } ; // 报错
2. 对象解构默认值
// 变量的非 undefined 的值可以取到
let { name, age = 28 } = { name:"Tom", age:18 };
console.info( name, age ); // Tom , 18
// 变量取到了 undefined ,则返回默认值
let { name, age = 28 } = { name:"Tom" };
console.info( name, age ); // Tom , 28
let {name: a = "Tom", age: b = 28} = { name :"Lily"};
console.info( a, b ); // Lily, 28
// 右侧一个值都没有
let {name: a = "Tom", age: b = 28} = {};
console.info( a, b ); // Tom, 28
3. 对象作为函数参数
Object 对象可以作为函数参数。
let addFun = function( obj ){
let sum = obj.a + obj.b ;
console.info( sum );
};
addFun({
a:100,
b:200
}); // 300
为了设置 a ,b 默认值。利用对象解构,设置形参:
// 实参和形参会进行解构操作。
let addFun = function( { a=100, b=100 } ){
let sum = a + b ;
console.info( sum );
};
addFun({
a:100,
b:200
}); // 300。a ,100 ; b,200
addFun({ a:50 }); // 150。a,50; b 被省略,取默认值 100 。
addFun({ }); // 200。a , b 均取默认值
addFun(); // Cannot read property 'a' of undefined
上面代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只有当函数 addFun 的参数是一个对象时,变量 a 和 b 才会通过解构赋值生成。
如果函数 addFun 调用时没提供参数,变量 a 和 b 就不会生成,从而报错。
addFun(); // 报错。Uncaught TypeError: Cannot read property 'a' of undefined
相当于在执行代码
{ a=100, b=100 } = undefined ;
试图对 undefined 进行对象结构,会报错。
通过提供函数参数的默认值,就可以避免这种情况。
let addFun = function( { a=100, b=100 } = {} ){
let sum = a + b ;
console.info( sum );
};
addFun( ); // 200。
参数默认值是 {} 。{} 会解构给 a,b 赋值。a,b 解构的值都是 undefined,所以取 a,b 均会获取解构的默认值 100。
四、对非对象进行解构
如果解构的值为 null、undefined,会得到一个类型错误提示。
如果你的值为数字、布尔值、字符串,会得到一个 undefined。
如果等号左侧的变量名为 Object.prototype 上的方法名称时,返回的就是方法实例。
let {a} = null ; // 报错
let {a} = undefined ; // 报错
let { age:a } = undefined ; // 报错
let {a} = 13 ; // a = undefined
let { toString } = 123 ; // toString() { [native code] }