一、什么是解构赋值
在ES6及以后版本的JavaScript中,允许按照一定的模式从数组和对象中提取值,然后再对变量进行赋值,这种操作就被称为解构赋值,解构赋值相当于两个步骤,先是解构分解数据结构,然后再为变量赋值。
1.数组的解构赋值
一个小栗子
{
let a = 1;
let b = 2;
let c = 3;
// 上面的代码等同于下面
let [a,b,c] = [1,2,3];
console.log(a,b,c); // 输出1 2 3
}
可以看出有了解构赋值的新特性后,对变量的赋值就更加方便了。本质上,解构赋值的语法属于模式匹配,只要等号两边的匹配模式相同(如数组与数组,对象与对象),那么左边的变量就会被赋予相对应的值,顺序是从左往右的。
如果没有匹配成功
{
let [x,y,z,a] = [1,2,3];
console.log(x,y,z,a); // x,y,z会输出1 2 3,而a会输出undefined
let [arr] = 1;
console.log(arr); // 如果赋值不是数组,模式不同的话就会报错
}
如果解构不成功的话,那么变量的值就会等于undefined,没有定义,没有默认值。
2.数组解构的默认值
ES6内部使用严格相等运算符(===)来判断一个位置是否有值,如果一个数组的成员不严格等于undefined,那么默认值就不会生效,优先使用右边的值。
解构赋值允许指定相应的默认值。
{
let [foo = true] = [];
let [x,y = 'web'] = ['a'];
let [m,n = 'web'] = ['a',undefined];
console.log(foo); // 输出true
console.log(x,y); // 输出a web
console.log(m,n); // 输出a web
}
可以看到如果有默认值的话那么在匹配不成功时就会使用默认值。
默认值可以引用解构赋值的其他变量,但该变量必须已经声明,否则就会报错。
{
let [x = 1,y = x] = []; // x为1,y也为1
let [x = 1,y = x] = [2]; // x为2,y为2
let [x = 1,y = x] = [2,3]; // x为2,y为3
let [x = y, y = 1] = [] // 报错
}
数组的解构赋值当中,优先匹配等号右边的值,所以第二个和第三个的结果是那样的,但如果默认值当中的变量没有在声明后使用的话就会报错,如第四个例子y就在声明前使用了,因此就会报一个未在初始化之后使用的错误。
3.对象的解构赋值
对象的解构赋值与数组的方式不同,数组的元素是按次序排列的,有相应的索引,变量的取值是由它所在的位置决定的。而对象的属性是没有次序的,因此变量必须与属性同名才能取到正确的值。
对象解构允许我们使用变量的名字匹配对象的属性,匹配成功将对象属性的值赋值给变量。
{
// 在ES5当中获取属性中的值得方法
let person = {name : '张三' , age : 20 , sex : '男'};
person.name;
person.age;
person.sex;
// 在ES6及以后版本中的方法
let {name,age,sex} = {name : '张三' , age : 20 , sex : '男'};
console.log(name,age,sex); // 输出张三,20,男
// 上面的方法相当于下面这样的简写
let {name:name,age:age,sex:age} = {name : '张三' , age : 20 , sex : '男'};
}
左边结构里的第一个name就是匹配右边的name属性,而第二个name才是变量的名字,将匹配到的name属性的值赋值给左边的name变量。这就是对象解构赋值的机制:先找到同名的属性,然后再赋值给对于的变量,真正被赋值的是后面的变量。
对象结构赋值也可以用于嵌套结构的对象。
{
let obj = {};
let arr = [];
// 匹配左右两边属性名为foo的值,把123赋给了左边foo里的obj.prop属性,bar则是赋值给了arr数组的第0个元素。
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
console.log(obj); // 输出{prop:123}
console.log(arr); // 输出[true]
}
这里加了括号是为了把这行代码当成表达式运行,否则就会报错。
对象解构赋值的默认值
对象的解构赋值也可以指定默认值,并且默认值生效的条件时对象的属性严格等于undefined(===undefined)。
{
let { x = 3 } = {}; // x为3
let { x, y = 5 } = { x: 1 }; // x为1,y为5
let { x: y = 5 } = {}; // 报错,x还没有定义
// 如果解构失败,变量的值就为undefined
let {foo} = { bar : 'a'} // foo为undefined
}
4.字符串的解构赋值
字符串的解构赋值,是将字符串看作一个类似于数组的对象。
{
let [a,b,c,d] = 'hello';
console.log(a,b,c,d); // 输出 h e l l
}
这样赋值就相当于是数组的索引方式,从左往右匹配
每个类似数组的对象都有一个length属性,因此还可以对length属性解构。
{
let [length : len] = 'hello';
console.log(len); // 输出5
}
相当于是对字符串的length属性设置了一个变量,这个变量就等于右边字符串的长度。
5.函数参数的解构赋值
函数调用时,会将实参传递给形参,这个过程其实也就是将实参赋值给形参。因此,函数的参数也可以使用结构赋值。
其规则与数组、对象的解构赋值是一样的,关键是参数用的是哪种解构赋值的方式。
一个小例子:
{
function a({name,age}){
console.log(name,age);
}
a({name : '小张',age : 20}); // 输出小张,20
function add({ x, y = 10 }) {
return x + y;
}
let r = add({ x: 20 });
console.log(r); // 输出 30
}
二、解构赋值的用途
1.交换变量的值
在ES6以前,没有结构赋值新特性时交换变量是这样的。
var a = 1
var temp;
var b = 2;
temp = a;
a = b;
b = temp;
// 有了解构赋值后
let x = 1, y = 3;
[x, y] = [y, x];
可以看出,用了解构赋值的特性后,在变量交换值上就简单了许多,易读性也很高,很方便。
2.从函数中返回多个值
解构赋值还可以用在函数的返回值当中,可以返回多个值,用数组或者对象的方法。
{
function func() {
return [1, 2, 3];
}
let [a, b, c] = func();
console.log(a,b,c); // 输出 1,2,3
function func2() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = func2();
console.log(foo,bar); // 输出 1,2
}
3.提取JSON数据
这个算是用处比较广泛的,JSON格式的数据我们会经常用到,因此这个时候用解构赋值的方法去获取JSON数据也是非常方便的。
{
let json = '{"id" : 42,"state" : "OK","d" : [233,666]}';
let data = JSON.parse(json); // 把字符串转为JSON格式的数据
let { id, state, d : num} = data;
console.log(id, state, num); // 输出 42,"OK",[233,666]
}
总结
可以看到在ES6当中获取属性值得方法比ES5及以前要简单了许多,无论是数组还是对象,都比以前更具有可读性,并且可以应用在许多场合,因此可以多多使用解构赋值这个新特性来实现各种获取值或者赋值。