1 作用于数组
1.1 解构赋值简介
解构赋值:按照一定的模式,从数组 / 对象中提取值,并对变量进行赋值
let [a, b, c] = [12, 5, 6]; // 左边的结构 = 右边的结构
console.log(a, b, c); // 12 5 6
本质上,这种写法属于 “模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
变量 = 值
let [a, b, c] = [12, [5, 6]];
console.log(a, b, c); // 12 [ 5, 6 ] undefined
let [a, [b, c]] = [12, [5, 6]];
console.log(a, b, c); // 12 5 6
let [a, [[b], c]] = [1, [[2], 3]];
console.log(a, b, c); // 1 2 3
变量 < 值 - - - 不完全解构:左边模式,只匹配右边模式的一部分。这种情况下,仍可成功解构
let [, , c] = "baz";
console.log(c) // "z"
let [x, , y] = [1, 2, 3];
console.log(x, y); // 1 3
let [x, y] = [1, 2, 3];
console.log(x, y); // 1 2
let [a, [b], c] = [1, [2, 3], 4];
console.log(a, b, c); // 1 2 4
变量 > 值 - - - 解构不成功,变量的值就等于
undefined
let [a] = []; // a 为 undefined
let [b, a] = [1]; // a 为 undefined
模式不匹配 - - - 报错 TypeError
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
1.2 默认值
我们可以给变量设置默认值,格式:变量名 = 默认值
let [a, b, c = "暂无数据"] = ["aa", "bb", "cc"]; // c 设置了默认值
console.log(a, b, c); // aa bb cc
let [a, b, c = "暂无数据"] = ["aa"]; // b 没有默认值
console.log(a, b, c); // aa undefined 暂无数据
ES6 使用严格相等运算符 ===
,来判断一个位置是否有值。所以,如果数值是 undefined
,就会使用默认值
// 我们也可以选择性使用 undefined,来使用默认值
let [a, b = "暂无数据", c] = ["aa", undefined, "cc"];
console.log(a, b, c); // aa 暂无数据 cc
// 如果传入的是 null,则不会使用默认值:undefined == null,undefined !== null
let [a, b = "暂无数据", c] = ["aa", null, "bb"];
console.log(a, b, c); // aa null bb
默认值可以是表达式
如果默认值是表达式,那这个表达式是惰性求值的,即只有在用到时,才会求值
function f() {
console.log(2); // 不会输出
return 2;
}
let [x = f()] = [1];
console.log(x); // 1
- 默认值生效时,才会调用
f()
function f() {
console.log(2);
return 2;
}
let [x = f()] = [];
console.log(x); // 2 2
默认值可以是解构赋值的其他变量
let [x = 1, y = x] = [1, 2]; // x = 1; y = 2
let [x = 1, y = x] = [2]; // x = 2; y = 2
let [x = 1, y = x] = []; // x = 1; y = 1
- 被引用的变量必须是已经声明了的,否则会报错:
let [x = y, y = 1] = []; // ReferenceError: Cannot access 'y' before initialization
y=1
之前的区域为 y
的暂时性死区
作用:可以很方便地交换两个值
let a = 12, b = 5;
console.log(a, b); // 12 5
[a, b] = [b, a];
console.log(a, b); // 5 12
2 作用于对象
2.1 对象的解构赋值
- 数组的元素是按次序排列的,变量的取值由它的位置决定
- 对象的属性没有次序,所以属性名必须对应,才能取到正确的值
- 对象的属性没有次序,所以不限获取的位置 & 次数
let obj = { // 定义对象
name: "superman",
age: 18
};
let {
name: nam,
age: ag
} = obj;
console.log(nam, ag); // superman 18
- 对于对象,解构赋值的内部机制,是先找到同名属性,再赋值给对应的变量。真正被赋值的是后面的变量
- 根据 ES6 的对象定义,属性名与变量名一样时,对象的解构赋值可以简写:
let {
name: name,
age: age,
sex: sex
} = obj;
console.log(name, age, sex); // superman 18 男
let {
name,
age,
sex
} = obj;
- 注意:对于对象,
let
后面的{}
是用于解构的,不会形成块级作用域,在同一作用域中不能重复let
let a;
let { a } = { a: "apple" }; // SyntaxError: Identifier 'a' has already been declared
2.2 默认值
变量默认值生效的条件:属性值严格等于 ===
undefined
let { x = 3 } = {};
console.log(x); // 3
上述 {}
中没有 x
属性,默认给变量 x
赋值为 3
let { x, y = 5 } = { x: 1 };
console.log(x, y); // 1 5
上述 { x: 1 }
中,找到属性 x
,再将属性值 1
赋值给变量 x
;找不到属性 y
,默认给变量 y
赋值为 5
let { x: y = 3 } = {}; // x 是属性名,y 才是变量
console.log(y); // 3
上述 {}
中,找不到属性 x
,默认给变量 y
赋值为 3
let { x: y = 3 } = { x: 5 }; // x 是属性名,y 才是变量
console.log(y); // 5
上述 { x: 5 }
中,找到属性 x
,将 属性值 5
赋给变量 y
2.3 数组的特殊解构
数组的本质是特殊的对象,所以可以对数组进行对象属性的解构
let arr = [1, 2, 3];
let { 0: first, [arr.length - 1]: last } = arr;
console.log(first, last); // 1 3
默认值可以是其他变量
let { x = 3, y = x } = {};
console.log(x, y); // 3 3
3 作用于基本类型的数据
3.1 字符串
- 字符串有
length
属性,可以看做是类数组(对象)
const [a, b, c, d, e] = 'hello';
console.log(a, b, c, d, e) // h e l l o
- 本质上,类数组是有
length
属性的对象,因此还可以对length
属性进行解构赋值
let {
length: len
} = 'hello';
console.log(len); // 5
- 说明类数组可以使用正常的数组 / 对象的解构方式
3.2 数值、布尔值
let toString = Object.prototype.toString;
// 等价于
let {toString} = Object.prototype; // 对象的解构赋值
- 解构赋值时,如果等号右边是数值 / 布尔值,会先转为包装类的对象
let { toString: s } = 123; // 123 转成对象 Number(123)
console.log(s === Number.prototype.toString) // true
let { toString: s } = true; // true 转成对象 Boolean(true)
s === Boolean.prototype.toString // true
4 作用于函数
4.1 配合函数参数
说到底就是数组 / 对象的解构赋值,只是写到函数参数的位置而已
- 数组参数
function add([a, b]) {
console.log(a + b);
}
add([2, 3]) // 5
- 对象参数:
function fn({ a, b }) {
console.log(a, b); // 1 2
}
fn({ a: 1, b: 2 })
对象参数的默认值 & 对象参数属性的默认值
function move({
x = 0, // 对象参数的属性设置了默认值
y = 0
} = {}) { // 对象参数设置了默认值
console.log([x, y]);
}
move({ x: 3, y: 8 }); // [3, 8]
move({ x: 3 }); // [3, 0]
// 因为对象参数的属性设置了默认值,所以可以传空对象
move({}); // [0, 0]
// 因为对象参数设置了默认值,所以可以不传参
move(); // [0, 0]
- 如果只有对象参数的默认值:
function move({ x, y } = { x: 0, y: 0 }) { // 这里只给对象参数设置了默认值
return [x, y]; // 没有给对象参数的属性设置默认值
}
move({ x: 3, y: 8 }); // [3, 8]
move({ x: 3 }); // [3, undefined]
// 没有设置对象参数的默认属性值,所以输出 undefined
move({}); // [undefined, undefined]
// 需要不传参才能使用默认对象参数
move(); // [0, 0]
4.2配合函数返回值
function getPos() {
return {
left: 10,
_top: 20 // top 是保留字,不能用作变量名
};
}
let {
left,
_top
} = getPos();
console.log(left, _top); // 10 20
5 解构嵌套的结构
let obj = { // 解构对象,则按属性名赋值
p: [
'Hello',
{
y: 'World'
}
]
};
let {
p, // ["Hello", {…}]
p: [
a, b // Hello {y: "World"}
],
p: [
a, { // Hello
y // World
}
]
} = obj;