数组
取右侧数组对应下标值,如果对应下标没值,则返回 undefined
let [a, [b], c, d] = [1,[2, 3], 4]
a // 1
b // 2
c // 4
d // undefined
let [a, ...b] = [1,2,3,4]
a // 1
b // [2,3,4]
let [a, b, ...c] = [1]
a // 1
b // undefined
c // []
let [a,b] = [1, null]
a // 1
b // null , 因为 null 不全等(===)于 undefined
如果等号的右边不是数组或者严格地说,不是可遍历的结构,那么将会报错。
// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
上面的语句都会报错,因为等号右边的值,要么转为对象以后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)。
对象
列子:{模式: 变量} = {模式},真正被赋值的是变量,匹配的是模式
**交换变量的值**
let x = 1;
let y = 2;
[x, y] = [y, x];
x // 2
y // 1
// 例一
let {a ,b} = {a:123, b:456}
a // 123
b // 456
上面等价于
let {a:a ,b:b} = {a:123, b:456}
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa" ,真正是给变量赋值
foo // error: foo is not defined , 模式只是对应右侧key来获取 value 值
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
// 例二
const { log } = console;
log('hello') // hello ,直接通过变量 log 执行 console.log 语句
如果解构失败,变量的值等于undefined。
let {foo} = {bar: 'baz'};
foo // undefined
上面代码中,等号右边的对象没有foo属性,所以变量foo取不到值,所以等于undefined。
如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错。
// 报错
let {foo: {bar}} = {baz: 'baz'};
上面代码中,等号左边对象的foo属性,对应一个子对象。该子对象的bar属性,解构时会报错。原因很简单,因为foo这时等于undefined,再取子属性就会报错。
函数参数解构赋值
function test([a, b]){
return a+b
}
test([1,2]) //3
下面是另一个例子。
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]
默认值
注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
数组默认值
列一
let [a, b=1] = [2, 3]
a // 2
b // 3
列二
let [a, b=1] = [2]
a // 2
b // 1
列三
let [a, b=1] = [2, null]
a // 2
b // null , 默认值就不会生效,因为`null`不严格等于`undefined`
列四
let [a, b=1] = [2, undefined]
a // 2
b // 1
对象默认值
列一
let {a = 3} = {a:1}
a // 1
列二
let {a, b=3} = {a:1, b:2}
a // 1
b // 2
列三
let {a: b=3} = {a:1}
b // 1
列四, 默认值生效的条件是,对象的属性值严格等于`undefined`。
let {a = 3} = {a:undefined}
a // 3
let {a = 3} = {a:null}
a // null
let {a: b=3} = {a:undefined}
b // 3
列五
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
函数默认值
函数参数的解构也可以使用默认值。
function move({x = 0, y = 0} = {}) {
return [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] ,有传值,则{ x: 0, y: 0 }被替代为{x: 3},但y值右侧没有,所以返回undefined
move({}); // [undefined, undefined], 同理向上
move(); // [0, 0] ,直接没传任何参数进去,则取{ x: 0, y: 0 }
// 参数是一组有次序的值
function f([x, y, z]) {
return x+y+z
}
f([1, 2, 3]); // 6
// 参数是一组无次序的值
let obj = {aa: 3, y: 2, x: 1}
function f({x, y, aa:z}) {
return x+y+z
}
f(obj); // 6
上面代码是为函数move的参数指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种写法不同的结果。
undefined就会触发函数参数的默认值。
[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]
注意点
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
上面代码的写法会报错,因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
// 正确的写法
let x;
({x} = {x: 1});