变量的解构赋值
解构赋值是什么
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值
var/let [变量名称1,变量名称2,...] = 数组或对象
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
解构赋值失败
如果解构不成功,变量的值就等于undefined
解构赋值失败 - 定义变量的数量大于值的数量
let [m,n] = [1];
console.log(m, n);//1 undefined
不完全解构赋值
等号左边的模式,只匹配一部分的等号右边的数组
不完全解构赋值 – 定义变量的数量小于值的数量
解构依然可以成功
let [a,b] = [1,2,3];
console.log(a, b);//1 2
默认值
解构赋值允许指定默认值
解构赋值失败时,变量的默认值为undefined
默认值–指的就是在解构赋值失败时,重写undefined默认值
ES6 内部使用严格相等运算符(
===
),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined
,默认值才会生效
let [a=true] = [100];
console.log(a);//100
let [x, y = 100]= [10];
console.log(x,y);// 10 100
// ES6底层将为变量赋值的值与undefined进行比较(全等于)
let [m,n = 100]=[10,undefined];
console.log(m,n);
ES6底层将为变量赋值的值只是与
undefined
进行比较(null等没有意义的)
let [v,w=100] = [10,null]
console.log(v, w);//10 null
对象的解构赋值
解构不仅仅可以用于数组,也可以用于对象
let {x,y}={
x:10,
y:20
}
console.log(x, y);//10 20
对象的解构赋值
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
- 数组[] = 对象{}
let [m,n]= {
m:20,
n:10
}
console.log(m, n);// {(intermediate value)(intermediate value)} is not iterable
- 对象{} = 对象{函数}
let {m,n}={
m:100,
n:function () {
return 10;
}
}
console.log(m, n());// 100 10
- 数组[] = 数组[函数]
let [m, n] =[100,function(){return 10;}];
console.log(m,n());// 100 10
- 对象{} = 对象{对象,函数}
let {m,n}={
m: {
name:'张无忌'
},
n:function () {
return 10;
}
}
console.log(m.name,n());// 张无忌 10
- 数组[] = 数组[对象,函数]
let [m, n] =[{name:'张无忌'},function(){return 10;}];
console.log(n()); // 10
- 数组[] = 数组[数组]
let [a,b] = [[1,2],3]
console.log(a, b);//[ 1, 2 ] 3
let [[a,b],c] = [[1,2],3]//等价于 let [a,b,c] = [1,2,3]
console.log(a, b, c);// 1 2 3
解构赋值失败
let {m,n}={
m:100
}
console.log(m, n);//100 undefined
如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错
// 报错
let {x: {y}} = {y: '123'};
默认值
对象的解构赋值也可以指定默认值。默认值生效的条件是对象的属性值全等于undefined。
let {x,y=1}={
x:100
}
console.log(x, y);// 100
注意点(参考阮一峰的ECMAScript 6 入门)
- 如果要将一个已经声明的变量用于解构赋值,必须非常小心。
// 错误的写法
let x;
{x} = {x: 1};
console.log(x)
// 正确的写法
let x;
({x} = {x: 1});
console.log(x)
因为 JavaScript 引擎会将
{x}
理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
- 解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式
({} = [true, false]);
({} = 'abc');
({} = []);
- 由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
console.log(first, last);// 1 3
数组
arr
的0
键对应的值是1
,[arr.length - 1]
就是2
键,对应的值是3
字符串的解构赋值
字符串被转换成了一个类似数组的对象
let [a,b,c]="xyz"
console.log(a, b, c);// x y z
let [a,b,c]="xxyyzz"
console.log(a, b, c);// x x y
let [a,b,c]="大前端了"
console.log(a, b, c);// 大 前 端
数值与布尔值的解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象.
规则
:
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错。
let num = new Number(100);
console.log(num.toString());// 100
//数字值或布尔值的解构赋值 - 先将数字值或布尔值转换成对象类型
let{toString:m}=100;//100本身就是number类型对象
console.log(m)// [Function: toString]
let {toString:x}=true;
console.log(x === Boolean.prototype.toString());//true
函数参数的解构赋值
函数参数的解构赋值
//函数定义 – 形参,相当于在函教作用域中定义了一个局部变量(没有赋值)
function fn(a,b) {
console.log(a, b);//10 20
}
//函数调用 – 实参,相当于在函数作用域中为定义的变量进行赋值
fn(10,20)
形参传入什么格式在实参用相同的格式进行匹配
默认值
function move({x = 0, y = 0} = {}) {
return [x, y];
}
console.log(move({x: 3, y: 8})); // [3, 8]
console.log(move({x: 3})); // [3, 0]
console.log(move({})); // [0, 0]
console.log(move()); // [0, 0]
小括号的问题
小括号的问题
解构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,一个式子到底是模式,还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才能知道。
由此带来的问题是,如果模式中出现圆括号怎么处理。ES6 的规则是,只要有可能导致解构的歧义,就不得使用圆括号。
但是,这条规则实际上不那么容易辨别,处理起来相当麻烦。因此,建议只要有可能,就不要在模式中放置圆括号。
不能使用小括号的情况
- 变量声明语句
// 全部报错
let [(a)] = [1];
let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
let { o: ({ p: p }) } = { o: { p: 2 } };
- 函数参数
函数参数也属于变量声明,因此不能带有圆括号
// 报错
function f([(z)]) { return z; }
// 报错
function f([z,(x)]) { return x; }
- 赋值语句的模式
// 全部报错
({ p: a }) = { p: 42 };
([a]) = [5];
可以使用它小括号的情况
赋值语句的非模式部分,可以使用小括号
[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确
解构赋值的用途
- 交换变量的值
解构赋值可用于两个变量之间交换值。
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x, y);// 2 1
-
从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。如果使用解构赋值的话,很容易实现函数返回多个值
- 返回一个数组
function fn() { return [1, 2, 3]; } let [a, b, c] = fn(); console.log(a, b, c);// 1 2 3
- 返回一个对象
function fn1() { return { a1: 1, b2: 2 }; } let { a1, b2 } = fn1(); console.log(a1, b2);// 1 2
-
函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
- 参数是一组有次序的值
function f([a, b, c]) { console.log(a, b, c); // 1 2 3 } f([1, 2, 3]);
- 参数是一组无次序的值
function f({q, w, e}) { console.log(q, w, e); // 3 2 1 } f({e:1, w:2, q:3});
-
函数参数的默认值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
} = {}) {
// ... do stuff
};
指定参数的默认值,就避免了在函数体内部再写
var foo = config.foo || 'default foo';
这样的语句。
- 提取JSON数据
解构赋值对于提取JSON对象中的数据内容尤其有用。
let jsonData = {
name: "张无忌",
age: 11,
job: "教主"
};
let { name, age, job } = jsonData;
console.log(name, age, job);
// 张无忌 11 教主