ECMA - 解构赋值

63 阅读4分钟

数组

取右侧数组对应下标值,如果对应下标没值,则返回 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的参数指定默认值,而不是为变量xy指定默认值,所以会得到与前一种写法不同的结果。

undefined就会触发函数参数的默认值。

[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]

注意点

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

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});