03【解构赋值】
03【解构赋值】
1.数组的解构赋值
1.1 原理
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
以前,为变量赋值,只能直接指定值。
let a = 1;
let b = 2;
let c = 3;
ES6 允许写成下面这样。
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
模式(结构)匹配
[] = [1, 2, 3];
索引值相同的完成赋值
const [a, b, c] = [1, 2, 3];
举例
const [a, [, , b], c] = [1, [2, 3, 4], 5]; console.log(a, b, c); // 1 4 5
1.2 数组解构赋值的默认值
(1)默认值的基本用法
const [a, b] = [];
console.log(a, b); // undefined undefined
// ---------------------------------------
const [a = 1, b = 2] = [];
console.log(a, b); // 1 2
(2)默认值的生效条件
只有当一个数组成员严格等于 (===) undefined 时,对应的默认值才会生效。
const [a = 1, b = 2] = [3, 0]; // 3 0
const [a = 1, b = 2] = [3, null]; // 3 null
const [a = 1, b = 2] = [3]; // 3 2
(3)默认值表达式
如果默认值是表达式,默认值表达式是惰性求值的(即:当无需用到默认值时,表达式是不会求值的)
const func = () => {
return 24;
};
const [a = func()] = [1]; // 1
const [b = func()] = []; // 24
1.3 数组解构赋值的应用
(1)arguments
function func() {
const [a, b] = arguments;
console.log(a, b); // 1 2
}
func(1, 2);
(2)NodeList
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>NodeList</title>
</head>
<body>
<p>1</p>
<p>2</p>
<p>3</p>
<script>
const [p1, p2, p3] = document.querySelectorAll('p');
console.log(p1, p2, p3);
/*
<p>1</p>
<p>2</p>
<p>3</p>
*/
</script>
</body>
</html>
(3)函数参数的解构赋值
const array = [1, 1];
// const add = arr => arr[0] + arr[1];
const add = ([x = 0, y = 0]) => x + y;
console.log(add(array)); // 2
console.log(add([])); // 0
(4)交换变量的值
let x = 2, y = 1;
// 原来
let tmp = x;
x = y;
y = tmp;
// 现在
[x, y] = [y, x];
// 理解:[x, y] = [2, 1]
console.log(x, y);
// 1 2
(5)跳过某项值使用逗号隔开
在解构数组时,可以忽略不需要解构的值,可以使用逗号对解构的数组进行忽略操作,这样就不需要声明更多的变量去存值了:
var [a, , , b] = [10, 20, 30, 40];
console.log(a); // 10
console.log(b); // 40
上面的例子中,在 a、b 中间用逗号隔开了两个值,这里怎么判断间隔几个值呢,可以看出逗号之间组成了多少间隔,就是间隔了多少个值。如果取值很少的情况下可以使用下标索引的方式来获取值。
(6)剩余参数中的使用
通常情况下,需要把剩余的数组项作为一个单独的数组,这个时候我们可以借助展开语法把剩下的数组中的值,作为一个单独的数组,如下:
var [a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]
在 rest 的后面不能有 逗号 不然会报错,程序会认出你后面还有值。...rest
是剩余参数的解构,所以只能放在数组的最后,在它之后不能再有变量,否则则会报错。
1.4 必须要分号的两种情况
// 1. 立即执行函数
// ;(function () { })();
// (function () { })();
// 2. 使用数组解构的时候
// const arr = [1, 2, 3]
const str = 'pink';
[1, 2, 3].map(function (item) {
console.log(item)
})
let a = 1
let b = 2
;[b, a] = [a, b]
console.log(a, b)
2.对象的解构赋值
2.1 原理
对象的解构和数组基本类似,对象解构的变量是在 {}
中定义的。对象没有索引,但对象有更明确的键,通过键可以很方便地去对象中取值。在 ES6 之前直接使用键取值已经很方便了:
var obj = { name: 'imooc', age: 7 };
var name = obj.name; // imooc
var age = obj.age; // 7
但是在 ES6 中通过解构的方式,更加简洁地对取值做了简化,不需要通过点操作增加额外的取值操作。
var obj = { name: 'imooc', age: 7 };
var { name, age } = obj; // name: imooc, age: 7
在 {}
直接声明 name 和 age 用逗号隔开即可得到目标对象上的值,完成声明赋值操作。
- 模式(结构)匹配
{} = {};
- 属性名相同的完成赋值
const {name, age} = {name: 'jerry', age: 18};
或const {age, name} = {name: 'jerry', age: 18};
2.2 对象解构赋值的默认值
对象的属性值严格等于 undefined 时,对应的默认值才会生效。
如果默认值是表达式,默认值表达式是惰性求值的。
对象的默认值和数组的默认值一样,只能通过严格相等运算符(===)来进行判断,只有当一个对象的属性值严格等于 undefined
,默认值才会生效。
var {a = 10, b = 5} = {a: 3}; // a = 3, b = 5
var {a = 10, b = 5} = {a: 3, b: undefined}; // a = 3, b = 5
var {a = 10, b = 5} = {a: 3, b: null}; // a = 3, b = null
所以这里的第二项 b 的值是默认值,第三项的 null === undefined
的值为 false,所以 b 的值为 null。
2.3 重命名属性
在对象解构出来的变量不是我们想要的变量命名,这时我们需要对它进行重命名。
var {a:x = 8, b:y = 3} = {a: 2};
console.log(x); // 2
console.log(y); // 3
这里把 a 和 b 的变量名重新命名为 x 和 y。
2.4 对象解构赋值的应用
(1)对象作为函数参数
// 之前
const logPersonInfo = user => console.log(user.name, user.age);
logPersonInfo({name: 'jerry', age: 18});
// 之后
const logPersonInfo = ({age = 21, name = 'ZJR'}) => console.log(name, age);
logPersonInfo({name: 'jerry', age: 18}); // jerry 18
logPersonInfo({}); // ZJR 21
(2)复杂的嵌套(主要是缕清逻辑关系即可)
const obj = {
x: 1,
y: [2, 3, 4],
z: {
a: 5,
b: 6
}
};
// ----------------------------------------------------
const {x, y, z} = obj;
console.log(x, y, z); // 1 [ 2, 3, 4 ] { a: 5, b: 6 }
// ----------------------------------------------------
const {y: [, y2]} = obj;
console.log(y2); // 3
console.log(y); // 报错
// ----------------------------------------------------
const {y: y, y: [, y2]} = obj;
console.log(y2); // 3
console.log(y); // [ 2, 3, 4 ]
// ----------------------------------------------------
const {y, y: [, y2], z, z: {b}} = obj;
console.log(y2); // 3
console.log(y); // [ 2, 3, 4 ]
console.log(z); // { a: 5, b: 6 }
console.log(b); // 6
(3)剩余参数中的使用
在对象的解构中也可以使用剩余参数,对对象中没有解构的剩余属性做聚合操作,生成一个新的对象。
var {a, c, ...rest} = {a: 1, b: 2, c: 3, d: 4}
console.log(a); // 1
console.log(c); // 3
console.log(rest); // { b: 2, d: 4 }
对象中的 b、d 没有被解构,通过剩余参数语法把没有解构的对象属性聚合到一起形成新的对象。
2.5 注意点
(1)如果要将一个已经声明的变量用于解构赋值,必须非常小心。
// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
上面代码的写法会报错,因为 JavaScript 引擎会将{x}
理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
// 正确的写法
let x;
({x} = {x: 1});
上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行。关于圆括号与解构赋值的关系,参见下文。
(2)解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式。
({} = [true, false]);
({} = 'abc');
({} = []);
上面的表达式虽然毫无意义,但是语法是合法的,可以执行。
(3)由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
上面代码对数组进行对象解构。数组arr
的0
键对应的值是1
,[arr.length - 1]
就是2
键,对应的值是3
。
3.字符串的解构赋值
既可以用数组的形式来解构赋值,也可以用对象的形式来解构赋值。
// 数组形式解构赋值
const [a, b, , , c] = 'hello';
console.log(a, b, c); // h e o
// 对象形式解构赋值
const {0: a, 1: b, 4: o, length} = 'hello';
console.log(a, b, o, length); // h e o 5
4.数值和布尔值的解构赋值
只能按照对象的形式来解构赋值。
(会先自动将等号右边的值转为对象)
// 先来复习一下将数值和布尔值转化为对象
console.log(new Number(123));
console.log(new Boolean(true));
// 转化后的对象里没有任何的属性(没有 123 这个属性,也没有 true 这个属性)和方法,
// 所有的属性和方法都在它的继承 __proto__ 中,比如 toString 方法就是继承来的。
// 里面的值只能是默认值,继承的方法倒是可以取到
const {a = 1, toString} = 123;
console.log(a, toString); // 1 [Function: toString]
// 里面的值只能是默认值,继承的方法倒是可以取到
const {b = 1, toString} = true;
console.log(b, toString); // 1 [Function: toString]
知道有这回事即可,一般都用不到,因为没太大意义。
5.undefined 和 null 没有解构赋值
由于 undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错。
6.小结
本节讲解了 ES6 解构赋值的使用方法,总结下来一共有以下几点:
- 解构赋值一般针对对象和数组,如果解构对象是
undefined
或是null
都会报错; - 默认值的生效条件是,只有当解构的对象的值是严格模式下的
undefined
的情况下,默认值才会生效; - 可以不借助中间变量来交换两个值;
- 在解构复杂的数据解构时,注意声明的对象要和目标的对象有着相同的解构形式,才能去解构目标对象。