你真的了解es6中的解构吗

927 阅读6分钟

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。如果解构失败,变量的值等于undefined,注意这句很重要!!

首先我们来看函数的解构

首先我们来看函数的解构函数的解构

function add([x, y]){
  return x + y;
}

add([1, 2]); // 3
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]

ok,我们来看两个易错的函数解构

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]

上面代码中,函数move的参数是一个对象,通过对这个对象进行解构,得到变量xy的值。如果解构失败,xy等于默认值。

但是我们来看这段代码

function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

上面代码是为函数move的参数指定默认值,而不是为变量xy指定默认值,所以会得到与前一种写法不同的结果。 理解这段代码的关键在于如何解析函数参数的解构赋值和默认值设置。让我们一步步分析 move 函数的不同调用情况。

function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

这里的函数定义使用了对象解构赋值,并为整个对象指定了一个默认值 { x: 0, y: 0 }。这意味着如果调用 move 函数时没有提供参数,或者提供的参数不是对象(例如 nullundefined),那么将使用默认的对象 { x: 0, y: 0 }

1. move({x: 3, y: 8})

move({x: 3, y: 8}); // [3, 8]
  • 传入参数{ x: 3, y: 8 }
  • 解构过程
    • 解构 { x: 3, y: 8 } 对象,提取属性 xy
    • x 被赋予 3y 被赋予 8
  • 返回值[3, 8]

2. move({x: 3})

move({x: 3}); // [3, undefined]
  • 传入参数{ x: 3 }
  • 解构过程
    • 解构 { x: 3 } 对象,提取属性 xy
    • x 被赋予 3
    • y 没有在传入的对象中找到,因此它被赋予 undefined(注意这里不使用默认值 { x: 0, y: 0 } 中的 y)。
  • 返回值[3, undefined]

3. move({})

move({}); // [undefined, undefined]
  • 传入参数{}(空对象)
  • 解构过程
    • 解构 {} 对象,提取属性 xy
    • 由于空对象中没有任何属性,xy 都被赋予 undefined
  • 返回值[undefined, undefined]

4. move()

move(); // [0, 0]
  • 传入参数:未提供任何参数(即 undefined
  • 解构过程
    • 因为没有提供参数,所以使用默认对象 { x: 0, y: 0 }
    • 解构 { x: 0, y: 0 } 对象,提取属性 xy
    • x 被赋予 0y 被赋予 0
  • 返回值[0, 0]
  1. 默认值应用条件:默认值 { x: 0, y: 0 } 只会在传入参数是 undefined 或者完全未提供时才生效。如果提供了对象,但该对象缺少某些属性,则这些属性会被赋予 undefined,而不是默认值。

  2. 解构行为:对于提供的对象,解构会严格按照对象的结构进行。如果某个属性不存在,则该变量会被赋予 undefined

  3. 空对象处理:当传入的是一个空对象 {} 时,所有未明确指定的属性都会被赋予 undefined,因为解构不会回退到默认对象。

数组的解构赋值

基本用法

传统的变量赋值方式较为冗长:

let a = 1;
let b = 2;
let c = 3;

使用解构赋值后,可以更加简洁地完成相同的操作:

let [a, b, c] = [1, 2, 3];

这种写法本质上属于“模式匹配”,只要等号两边的模式一致,左边的变量就会被赋予对应的值。

嵌套数组解构

解构赋值支持嵌套结构,可以处理更复杂的数组层次:

let [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(foo); // 1
console.log(bar); // 2
console.log(baz); // 3
省略元素与默认值

你还可以选择性地忽略某些元素,并为未定义的元素设置默认值:

let [x, , y] = [1, 2, 3]; // x=1, y=3
let [head = 'default', ...tail] = ['hello']; // head='hello', tail=[]

如果解构不成功,变量的值会等于 undefined

let [foo] = [];
console.log(foo); // undefined
默认值

解构赋值允许指定默认值,只有当属性严格等于 undefined 时,默认值才会生效:

let [x = 1] = [undefined];
console.log(x); // 1

let [x = 1] = [null];
console.log(x); // null

对象的解构赋值

基本用法

对象的解构赋值同样遵循模式匹配的原则,但变量必须与属性同名才能取到正确的值:

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
console.log(foo); // "aaa"
console.log(bar); // "bbb"

如果不完全解构,即只提取部分属性,解构依然可以成功:

let { baz } = { foo: 'aaa', bar: 'bbb' };
console.log(baz); // undefined
属性别名

如果需要为属性指定不同的变量名,可以通过冒号来实现:

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
console.log(baz); // "aaa"

什么叫对象属性的默认值,我们来看看这个例子

var { x: y = 3 } = { x: 5 };
console.log(y); // 输出: 5
  1. 对象解构{ x: y = 3 } 是解构模式,它表示从右边的对象中提取 x 属性的值,并将其赋给左边的变量 y

  2. 默认值= 3 表示如果解构时 x 属性不存在或其值为 undefined,则变量 y 的值将被设置为 3

  3. 右侧对象{ x: 5 } 是你要解构的对象,它有一个属性 x,其值为 5

  4. 解构过程

    • 右侧对象存在 x 属性,并且它的值是 5
    • 因此,y 被赋予了 x 的值,即 5
    • 默认值 3 不会被使用,因为 x 已经有了明确的值。
  • 默认值生效条件:默认值只有在解构的目标属性严格等于 undefined 时才会生效。在这个例子中,x 的值是 5,不是 undefined,所以默认值 3 不会被使用。

例子

1. 当属性存在且有值时

var { x: y = 3 } = { x: 5 };
console.log(y); // 输出: 5

2. 当属性存在但值为 undefined

var { x: y = 3 } = { x: undefined };
console.log(y); // 输出: 3

3. 当属性不存在时

var { x: y = 3 } = {};
console.log(y); // 输出: 3

4. 当属性值为 null 或其他非 undefined 值时

var { x: y = 3 } = { x: null };
console.log(y); // 输出: null

var { x: y = 3 } = { x: 0 };
console.log(y); // 输出: 0

var { x: y = 3 } = { x: '' };
console.log(y); // 输出: ''

这些例子展示了默认值在不同情况下的行为,帮助你更好地理解和应用对象解构赋值中的默认值机制。

嵌套对象解构

对于嵌套的对象结构,解构赋值同样适用:

let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
};

let { p: [x, { y }] } = obj;
console.log(x); // "Hello"
console.log(y); // "World"
默认值

对象解构赋值也支持默认值:

var { x = 3 } = {};
console.log(x); // 3

字符串、数值和布尔值的解构赋值

字符串可以像数组一样进行解构赋值:

const [a, b, c, d, e] = 'hello';
console.log(a); // "h"
console.log(e); // "o"

数值和布尔值的解构赋值会先将其转换为对象:

let { toString: s } = 123;
console.log(s === Number.prototype.toString); // true

圆括号问题

解构赋值中圆括号的使用有一定的限制,通常建议避免在模式中放置圆括号以防止歧义。例如:

// 错误
let [(a)] = [1];

// 正确
({ p: a } = { p: 42 });