Skip to content

ES6 变量的解构赋值

导言

本章要注意, 对象的默认值解构的写法,是用等号:

js
var { x: y = 3 } = {}; // y = 3
var { x: y = 3 } = { x: 5 }; // y = 5

函数的默认值解构最优写法如下:

js
function f({ a: c = 1, b: d = 2 } = {}) {
  console.log(c, d);
}
// 或者
function f2({ a = 1, b = 2 } = {}) {
  console.log(a, b);
}

f(); // 1, 2
f({}); // 1, 2
f({ a: 10 }); // 10, 2
f({ b: 10 }); // 1, 10
f({ a: 10, b: 10 }); // 10, 10

1. 数组的解构赋值

1.情况 1

js
// x='a', y 为undefined, z 为[]
let [x, y, ...z] = ["a"];

2.情况 2

结构不成功为 undefined

js
let [foo] = [];

3.下面的例子会报错, 结构不成功

js
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

4.解构 Set 结构

js
let [x, y, z] = new Set(["a", "b", "c"]);
x; // 'a'
y; // 'b'
z; // 'c'

5.解构 Iterator 接口

js
function* fibs() {
  let a = 0;
  while (true) {
    yield a;
    a++;
  }
}
let [first, second, third] = fibs();
first; // 0
second; // 1
third; // 2

相当于:

js
const g = fibs();
g.next(); // {value: 0, done: false}
g.next(); // {value: 1, done: false}
g.next(); // {value: 2, done: false}

2.默认值

1.默认值结构

js
let [foo = true] = []; // foo = true;
let [x, y = "b"] = ["a", undefined]; // x='a', y='b'
let [x, y = "b"] = ["a", null]; // x='a' y=null

2.惰性求值

foo 函数不会执行

js
function foo() {
  console.log("foo");
}
const [x = foo()] = [1];

上面的代码等价于:

js
let x;
if ([1][0] === undefined) {
  x = foo();
} else {
  x = [1][0];
}

3.默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

js
let [x = 1, y = x] = []; // x=1; y=1
let [x = y, y = 1] = []; // 报错, y还没有声明

但是下面这种写法不会报错, 可以用惰性求值来理解, x 执行不到 x=y 这个语句

js
let [x = y, y = 1] = [5, 6]; // x=5; y=6

可以等价于:

js
let x;
if ([5, 6][0] === undefined) {
  x = y;
} else {
  x = [5, 6][0];
}

if ([5, 6][1] === undefined) {
  y = 1;
} else {
  y = [5, 6][1];
}

2. 对象的解构赋值

1.简写的形式

对象的解构赋值是下面形式的简写:

js
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };

2.机制

对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。

js
let { foo: baz } = { foo: "aaa", bar: "bbb" };
bar; // 'aaa'
foo; // error: foo is not undefined

对于嵌套结构的对象:

js
let obj = {
  p: ["Hello", { y: "World" }]
};
let {
  p: [x, { y }]
} = obj;
x; // 'Hello'
y; // 'World'

这个时候 p 是模式, 不是变量, 如果要把 p 作为变量赋值, 可以写成:

js
let {
  p,
  p: [x, { y }]
} = obj;

另外一个例子:

js
const node = {
  loc: {
    start: {
      line: 1,
      column: 5
    }
  }
};
const {
  loc,
  loc: { start },
  loc: {
    start: { line }
  }
} = node;

3.嵌套赋值

注意必须整个表达式用括号括起来

js
let obj = {};
let arr = [];
({ a: obj.p, b: arr[0] } = { a: 1, b: 2 });

不能再写成 let 的形式:

js
let obj = {};
let arr = [];
let {a:obj.p, b: arr[0]} = {a:1, b: 2};
// Uncaught SyntaxError: Identifier 'obj' has already been declared

4.解构报错

因为这时 c 为 undefined, 所以这个时候取 b 就会报错

js
let {
  c: { b }
} = { a: { b: 1 } };

5.对象的解构赋值可以取到继承的属性

js
const obj1 = {};
const obj2 = { foo: "bar" };
Object.setPrototypeOf(obj1, obj2); // 相当于 obj1.__proto__ = obj2;

const { foo } = obj1; // foo: 'bar'

另外一个例子:

js
var { push } = [];
push; // 也是继承来的属性;

默认值

注意, 默认值是用等号来表示的!!!

js
var { x = 3 } = {};
var { x: y = 3 } = { x: 5 }; // y = 3

所以对于函数的参数的对象解构,可以这么理解:

js
function f({ a = 3 }) {
  console.log(a);
}
f({});
f({ a: 1 });

默认值生效的条件是,对象的属性值严格等于 undefined。

注意点

如果要将一个已经声明的变量用于解构赋值,必须非常小心。 js 引擎会将 {x} 理解成一个代码块,从而发生语法错误,只有不将大括号放在行首,才可以解决;

js
let x;
{x} = {x: 10};
// 正确写法
({x} = {x: 1});

解构赋值允许等号左边的模式之中,不放置任何变量名。

js
({} = [true, false]);
({} = "abc");
({} = []);

数组是特殊的对象,也可以对数组进行解构;

js
let arr = [1, 2, 3];
let { 0: first, [arr.length - 1]: last } = arr;
// first = 1, last = 3

3. 字符串的解构赋值

字符串被转换成了一个类似数组的对象

js
const [a, b, c, d] = [1, 2, 3, 4];
// a =1; b = 2; c = 3; d = 4

解构赋值的注意点是, 只要等号右边的值不是对象或数组, 就先将他们转为对象。由于 undefined 和 null 无法转换为对象, 所以对它们进行解构会报错;

js
let { p } = undefined;
let { q } = null;

4. 函数的参数解构赋值

  1. 没有输入参数, 指定默认参数
js
function f(para = 1) {
  console.log(para); // 1
}
f();

下面的例子有参数, 相当于对象的解构: let {a = 10} = {a: 20};

js
function f({ a = 10 }) {
  console.log(a);
}
f({ a: 20 });
f(); // 参数啥都没传, 会报错

如何解决函数的参数啥都没传, 不会报错,并且可以默认值解构:

js
function f({ a, b } = { a: 1, b: 2 }) {
  console.log(a, b); // 1, 2
}
f();
f({ a: 1 }); // 1 undefined

上面的例子,若要解决f({a: 1}); 能输出默认值 b=2, 则写法如下:

js
function f({ a = 1, b = 2 } = {}) {
  console.log(a, b); // 20, 2
}
f({ a: 20 });

如果 ab 的名字我要改成 cd, 那么应该这么写, 最终实战的版本如下:

js
function f({ a: c = 1, b: d = 2 } = {}) {
  console.log(c, d);
}
f({ a: 20 });

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

js
[1, undefined, 3].map((x = "yes") => x);

用途

1.交换变量的值

js
let x = 1;
let y = 2;

[x, y] = [y, x];

2.遍历 Map 结构

js
const map = new Map();
map.set("first", "hello");
map.set("second", "world");

for (const [key, value] of map) {
  console.log(key + " is " + value);
}

注意, 如果 for...of 作用到数组, 那么解析的值是 item:

js
var arr = [5, 6];
for (const item of arr) {
  console.log(item); // 5 6
}

如果只想获取键名,或者只想获取键值,可以写成下面这样。

js
// 获取键名
for (let [key] of map) {
  // ...
}

// 获取键值
for (let [, value] of map) {
  // ...
}

3.输入模块的指定方法

js
const { SourceMapConsumer, SourceNode } = require("source-map");