变量的解构赋值
数组的解构赋值
let [a, b] = [1, 2]; // a = 1, b = 2
let [a, [[b], c]] = [1, [[2], 3]]; // a = 1, b = 2
let [ , , c] = [1, 2, 3]; // c = 3
let [a, ...b] = [1, 2, 3]; // a = 1, b = [2, 3]
let [a, b, ...c] = [1]; // a = 1, b = undefined, c = []
let [a, [b], c] = [1, [2, 3], 4]; // a = 1, b = 2, c = 4
我们可以看到,数组的解构赋值关键在于相同格式下的位置匹配。而且有一点我们需要关注,这里的数组并不是严格意义上的数组,而是拥有可迭代结构的数据(此部分与Iterator相关)。比如我们的arguments对象,我们也经常在写形参时直接调用解构赋值。
原生具备Iterator接口的数据结构有:
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
我们一个个来演示各自解构出来的分别是什么:
// Map解构:得到entry值
const map = new Map();
map.set(1, 'a');
map.set(2, 'b');
let [a, b] = map;
console.log(a); // [1, 'a']
console.log(b); // [2, 'b']
// Set解构:得到单个值
const set = new Set();
set.add(1);
set.add(2, 'b');
let [a, b] = set;
console.log(a); // 1
console.log(b); // 2
// String解构:得到单个字符
const str = '123';
const [a, b, c] = str;
console.log(a); // '1'
console.log(b); // '2'
// TypedArray解构
const typedArr = new Int8Array([1,2]);
let [a, b] = typedArr;
console.log(a); // 1
console.log(b); // 2
// arguments解构
function func(a, ...b) {
console.log(a); // 1
console.log(b); // [2, 3]
}
func(1,2,3);
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
// NodeList解构
const divs = document.getElementsByTagName('div');
const [a, b, c] = divs;
console.log(a); // <div>1</div>的节点对象
</script>
</body>
当然我们也可以自己实现迭代结构:
class MyIterator {
constructor(obj) {
this.data = obj;
}
[Symbol.iterator]() {
let index = 0;
let data= this.data;
let keysArr = Object.keys(data || {});
let length = keysArr.length;
return {
next() {
if (index < length) {
return {
done: false,
value: '解构:' + data[keysArr[index++]]
}
} else {
return { done: true};
}
}
}
}
}
const myIterator = new MyIterator({a: 'value-a', b: 'value-b'});
const [a, b] = myIterator;
console.log(a); // 解构:value-a
console.log(b); // 解构:value-b
一个经典例子:交换两值
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1
不知道有没有人和我一样,最开始疑惑:为什么a = b赋值完后,a变成了2,再b = a的话,那不就是b = 2了吗?
后来我换了种思考方式,我们应该把赋值左右两边区分开来,分别是赋值源和赋值目标,首先我们需要确定好赋值源的值,即[b ,a]等同于已经确定好了[2,1],然后再进行[a ,b] = [2,1]赋值。
对象的解构赋值
let { a, b } = { a: 1, b: 2 }; // a = 1, b = 2
let { a } = { b: 1, c: 2 }; // a = undefined
let { a: [b, { c }] } = { a: [1, { c: 2 }] }; // b = 1, c = 2
属性别名
let { a: hello, b } = { a: 1, b: 2 };
console.log(hello); // a失效,此时hello = 1
console.log(b); // 2
基础类型的解构
-
String:之前说了String类型是个迭代器结构,使用数组解构能得到对应位置的字符。而数组本质上也是个对象类型,因此String类型也能够使用对象解构。
-
Number/Boolean:遇到数字或者布尔值时,会转化成包装对象,然后再做对象解构。
let bool = true;
let { toString } = bool
console.log(toString.call(bool)); // 'true'
默认值
解构赋值可以添加默认值。
数组解构赋默认值
let [a, b = 2] = [1, undefined]; // x=1, y=2
需要注意的是ES6内部使用严格相等运算符(===)来判断一个值是否是undefined。如下例子,如果是null,则默认值失效,所以我们在写解构赋值默认值时需要注意自己的数据是否可能出现null。
let [a = 1, b = 2] = [undefined, null];
console.log(a); // 1
console.log(b); // null
对象解构赋默认值
同样与数组解构中判断undefined逻辑相同。
let { a = 1, b = 2} = {a: 3, b: null};
console.log(a); // 3
console.log(b); // null
如果又想使用别名,又想设置默认值,能同时写吗?答案是可以。
let { a: hello = 1, b = 2} = {a: 3, b: null};
console.log(hello); // a失效,此时hello = 3
console.log(b); // 2