0x01.概念
学习ES6结构赋值之前,先看一下MDN对解构赋值的官方解释:
解构赋值语法是一个 Javascript 表达式,这使得可以将值从数组或属性从对象提取到不同的变量中。
官方的概念较为简洁,从字面上我们很难看出到底什么是解构赋值,也无从知晓结构赋值的使用方法,那下面,就一起学习一下神奇的解构赋值表达式。
0x02.数组的解构赋值
01.一般用法
假设我们有一个数组[1,2,3,4],现在有一个需求将数组中的四个值分别赋给变量a,b,c,d。按照之前JavaScript中对数组操作的知识,我们大概会写成下面这个样子,两种形式只是在写法上有区别,本质上都是手动将数组中的值赋给各个变量:
let arr = [1,2,3,4];
let a = arr[0];
let b = arr[1];
let c = arr[2];
let d = arr[3];
// OR
let a = arr[0], b = arr[1], c = arr[2], d = arr[3];
console.log(a,b,c,d);
如果使用ES6提供的解构赋值来完成这个需求,就能够省略一定的代码量,如下面使用结构赋值的代码和上面代码段最后所达到的效果是一致的。
let arr = [1,2,3,4];
let [a,b,c,d] = [1,2,3,4];
console.log(a,b,c,d);
02.使用默认值
除了能够更便捷地将数组中的值赋给变量,我们还能在数组的解构赋值中使用默认值,使用默认值的规则是当被解构的数组能够完全匹配到解构的各个变量时,变量将会被赋予数组中的值,如果数组中的值索引少于待赋值的变量,则使用默认值,如下所示:
// 当被解构的数组能够完全匹配解构的各个变量时,变量将会使用数组中的值
let [a=1,b=3,c=5] = [2,4,6];
console.log(a,b,c); // 2 4 6
// 如果被解构的数组索引少于待赋值的变量,则变量将会使用默认值
let = [x=1,y=3,z=5] = [2,4];
console.log(x,y,z); // 2 4 5
在这里有一个额外思考的问题,使用默认值,解释器在执行程序的过程中是先将默认值赋给变量,若数组中有匹配值,再进行替换?还是先查询数组中有无匹配值,若没有,再将默认值赋给变量?我们可以通过一个实验来具体探究一下,下面程序中可以看出,当被解构的数组中存在能够与变量y相匹配的值时,若解释器会先将变量默认值赋予变量,则控制台会先输出文本'default value',在输出变量x与y的值。
let [x,y=(function(){console.log('default value'); return 5;})()] = [3, 4];
console.log(x,y);
然而控制台并未输出这一段文本。
现在我们将数组中的值改为不匹配的状态,再次测试程序:
let [x,y=(function(){console.log('default value'); return 5;})()] = [3];
console.log(x,y);
这一次控制台打印出了文本信息'default value',且变量y的值也是用了默认值5。
由此我们可以推测出解释器在执行含有默认值的数据解构赋值表达式时是先在被解构的数组中寻找与变量匹配的值,如果没有找到,再使用变量设置的默认值进行赋值操作。
03.使用省略赋值
在使用数组解构赋值的时候我们可以使用省略赋值的方式从数组中只取到我们希望得到的值而无须定义多个变量,如下所示:
let [,,x] = [1,2,3];
console.log(x); // 3
04.使用扩展运算符 ...
在数组的解构赋值操作中,我们可以使用扩展运算符 ... 将数组剩余的值作为一个子数组赋给最后一个变量。
let [x,y,...z] = [1,2,3,4,5];
console.log(x,y,z); // 1 2 [3,4,5]
需要注意的是,我们不能够将扩展运算符 ... 置于解构赋值的开头或中间,否则解释器将给出错误信息:Uncaught SyntaxError: Rest element must be last element.告知用户只能在最后一个元素上使用扩展运算符 ...,如下两种使用方式都是错误的:
let [...x,y,z] = [1,2,3,4,5];
// OR
let [x,...y,z] = [1,2,3,4,5];
05.数组解构赋值的特殊情况
我们可以使用数组解构赋值来解构类数组类型的值,例如:
let [x,y] = "ABC";
console.log(x,y); // A B
上面这个代码段中我们使用了解构赋值对一个字符串进行操作,解释器在执行的过程中,如果解构赋值运算的等号左边使用数组的解构赋值,等号右边不是一个数组的情况下,默认会将等号右边的值转换为一个类数组进行解构操作。字符串类型的值具有长度属性length,且各个字符都可以通过下标取到,也就是可迭代的数据类型,所以变量x,y可以取到字符串中的值A,B。但如果我们将右边字符串换成一个数字类型的值,由于数字是不可迭代的数据类型,解释器将会给出错误信息:Uncaught TypeError: 123 is not iterable,如下所示:
let [x,y] = 123;
console.log(x,y); // Uncaught TypeError: 123 is not iterable
0x03.对象的解构赋值
01.一般用法
对象的解构赋值中,我们按照如下代码段定义,可以将对象中的属性取出并赋值给我们定义的变量中。
let {name:mochaName, age:mochaAge} = {name: 'Mocha', age: 18};
console.log(mochaName,mochaAge); // Mocha 18
但如果我们所使用的变量名与待解构的对象中的属性值一致的话,我们可以直接省略掉变量名,写成如下省略写法的形式:
let {name, age} = {name: 'Mocha', age: 18};
console.log(name,age); // Mocha 18
这种写法完全等效于:
let {name:name, age:age} = {name: 'Mocha', age: 18};
console.log(name,age); // Mocha 18
02.使用默认值
同数组的解构赋值一样,对象的解构赋值同样可以对变量设置默认值,如下:
let {name,age = 20} = {name: 'Mocha'};
console.log(name, age); // Mocha 20
03.嵌套
我们可以使用嵌套解构赋值的方式将对象中的数组或子对象结构出来,下面举例说明,首先我们学习解构对象中嵌套数组的形式:
let {name,age,skills:[skill1,skill2,skill3]} = {name: 'Mocha', age: 18, skills: ['JavaScript','HTML','CSS']};
console.log(name,age,skill1,skill2,skill3); // Mocha 18 JavaScript HTML CSS
对象中嵌套对象的解构赋值方式与对象中嵌套数组的解构赋值相类似,如下:
let {name,age,skills:[js: skill1,html: skill2,css: skill3]} = {name: 'Mocha', age: 18, skills: {js: 'JavaScript',html: 'HTML',css: 'CSS'}};
console.log(name,age,skill1,skill2,skill3); // Mocha 18 JavaScript HTML CSS
04.对象解构赋值的特殊情况
与数组解构赋值相类似,当对象解构赋值操作时,等号右边不是一个对象,解释器在执行程序时将会默认将等号右边的值转换为对象类型然后再进行解构赋值操作,举例说明,当我们使用对象解构赋值如下对一个字符串进行操作:
let {a} = 'ABC';
console.log(a); // undefined
解释器会首先将字符串ABC转化为对象类型的值,即:Object('ABC'),然后再进行解构赋值操作,通过输出Object('ABC'),我们可以得到一个对象如下,其中并没有a这一属性值,故我们得出的结果为undefined。
由于数字类型可以通过Object转化,所以和数组的解构赋值不同,对象的解构赋值操作等号右边的操作数是可以为数字类型的。
0x04.在方法参数中使用解构赋值
在方法参数中使用解构赋值可以极大地简化我们程序的代码,提升开发的效率,下面我们一起学习一下如何在方法参数中使用解构赋值。
01.使用数组解构赋值
在方法的参数中使用数组解构赋值其结果就和直接使用一样,省去了一个一个去取数组中的值的操作,提升了开发的效率。
function test([a,b,c,...d]){
console.log(a,b,c,d);
}
test([1,2,3,4,5]); // 1 2 3 [4, 5]
02.使用对象解构赋值
在方法参数中使用对象的解构赋值也与直接使用并无区别。
function test({name,age}){
console.log(name, age);
}
test({name: 'Mocha', age: 18}); // Mocha 18
但此处需要注意的是,若在调用对象时并未传入参数,则解释器会抛出错误信息:Uncaught TypeError: Cannot destructure property `...` of 'undefined' or 'null',为了解决这一问题,这里我们使用设置默认值的方式,如下:
function test({name, age}={}){
console.log(name, age);
}
test(); // undefined undefined
这里我们解决了解释器的报错,再不传入参数的时候默认给方法一个空的对象{}作为参数,但此时我们希望name和age也能够在不传入参数的时候使用默认值,就引申出下面两种写法:
写法一:
function test({name,age}={name: 'Mocha', age: 18}){
console.log(name, age);
}
test(); // Mocha 18
写法二:
function test({name = 'Mocha',age = 18}={}){
console.log(name, age);
}
test(); // Mocha 18
两种写法在执行中都可以避免解释器的报错,且都可以达到我们想要的结果,但若我们将一个空白对象{}作为参数传入方法,那两种写法将会得出截然不同的结果,如下:
写法一:
function test({name,age}={name: 'Mocha', age: 18}){
console.log(name, age);
}
test({}); // undefined undefined
写法二:
function test({name = 'Mocha',age = 18}={}){
console.log(name, age);
}
test({}); // Mocha 18
这是因为两种在解释器执行程序中逻辑不同带来的结果,在写法一中,我们将空对象{}传入, 那么空对象将会直接替代参数的默认值{name:'Mocha',age:18},所以在后续的解构赋值操作中,解释器在空对象{}中无法取到name与age,故程序输出两个undefined。然而在第二种写法中,我们传入一个空对象仅仅只是替换参数默认值,在之后的解构赋值操作中,解释器无法取到name和age,依旧会使用默认值输出。
在实际的工作中,上面两种方式并没有孰优孰劣的分别,具体选用何种方式都是根据需求来决定的。
0x05.总结
解构赋值极大地优化了我们的开发过程,善用能够在一定程度上提升我们的开发效率,上面是我在学习ES6解构赋值一点点记录,如有描述或理解不正确的地方,还请不吝赐教!