# (二)变量的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。这种方式的出现大大提高了代码的扩展性

# 前言

为什么要把ES6系列总结出来呢,第一个是为了加深印象,第二个就是在之后的代码中尽量将能用的新特性都用上!

如果我们以ES6的前后来区分代码风格的话,那么有关变量的部分应该是这样的

// old
function newPoint(options) {
    const arg = options === undefined ? {} : options;
    const x = arg.x ? 0 : arg.x;
    const y = arg.y ? 0 : arg.y;
    const r = arg.r ? 10 : arg.r;
    console.log(x, y, r);
}

newPoint({x: 5, y: 5, r: 10});

// new
function _newPoint({ x = 0, y = 0, r = 10 }) {
    console.log(x, y, r);
}

_newPoint({ x: 5, y: 5, r: 10 });

从执行上来说,新旧并没有区别,或许你会觉得这并没有什么,除了看上去代码简洁之外并没有实际意义,事实却并非如此~

# 1.数组的解构赋值

首先看看最容易被人忽略的数组解构。

我们在获取获取数组内的元素时,大多是通过下标的方式arr[0],这种方式也确实方便,但针对特定的情况时,解构会大大提高你的效率

# 1.1 同时赋值多个变量

先来看这样一段代码:

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

当你需要使用ab...时,你可能会这么做。而数组的解构可以让你用更少的代码做到同样的事。

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

# 1.2 解构嵌套数组

解构嵌套数组,就是模式匹配,只要等号两边的层级结构相同,就可以拿到对应位置的值,这在某些特定的情况十分有用。

const arr = [1, [2, 3, [4, 5, 6]]];

const [a, [b, c, [d, e, f]]] = arr;
a //1
b //2
c //3
d //4
e //5
f //6

# 1.3 相同“模式”的不完全解构

在1.1和1.2小节中,我们总是完整的对应数组结构来获取数据,但当我们模式相同但变量没有完全对应时,会发生什么呢?

let [a, b, c] = [1, 2, 3, 4]; // 1 2 3
let [a, b, c, d] = [1, 2, 3]; // 1 2 3 undefined
let [a, [b, c, [d, e]]] = [1, [2, 3, [4, 5, 6]]]; // 1 2 3 4 5

# 1.4 解构的默认值

为了防止1.3小节中出现的解构得到的值存在undefined的情况(实际开发中,也需要兼容某些数据),我们可以对其设定默认值。

let [a = true] = [];
a // true

注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。

let [x = 1] = [undefined];
x // 1

let [x = 1] = [null];
x // null

上面代码中,如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined。

如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

function f() {
    console.log('aaa');
}

let [x = f()] = [];
// console.log(aaa)
// x

# 1.5 注意

  • 数组的解构是根据它的位置(模式)对应的
  • 解构操作允许有默认值,但一定是已经声明的。
  • 如果等号的右边不是数组(或者严格地说,不是可遍历的结构)那么将会报错

# 2.对象的解构赋值

对象的解构赋值应该是我们使用的最平凡的解构操作了,先来看看基本操作:

const { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

# 2.1 根据属性解构对象

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

如果变量名与属性名不一致,必须写成下面这样。

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"

let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj; // 起了个别名
f // 'hello'
l // 'world'

这实际上说明,对象的解构赋值是下面形式的简写:

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

模式foo的值复制给变量foo,真正被赋值的是后者,而不是前者。

注意:

  • 对象解构和数组解构一样,都支持嵌套解构
  • 对象解构同样支持默认值

# 3.函数参数的解构赋值

利用解构优化函数调用时中场景:

# 3.1 解构对象类型参数

function fetch(option){
    var name = option.name;
    var age = option.age;
    var like = option.like;
}

// ES6

function fetch({ name, age, like }) {
    // 参数经历了 let { name, age, like } = option
}

# 3.2 解构数组类型参数

const arr = [
    [1, 1],
    [2, 2],
    [3, 3],
    [4, 4]
];
const newArr = arr.map(([x, y]) => x + y);
newArr // [ 2, 4, 6, 8 ]

# 3.3 为参数设定默认值

设定默认值,可以避免许多拿不到数据的情况

function func({ name = '余光', age = 23, like = 'FE' }) {
    console.log(name, age, like);
}
const options = { name: '余光' }
func(options);

# 四、常见的使用场景

变量的解构赋值用途很多。

# 4.1 交换变量的值

let x = 1;
let y = 2;

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

上面代码交换变量 x 和 y 的值,这样的写法不仅简洁,而且易读,语义非常清晰。

# 4.2 从函数返回多个值

函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。

// array
function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();

// object
function example() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = example();

# 4.3 函数参数的定义

解构赋值可以方便地将一组参数与变量名对应起来。

// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);

// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});

# 4.4 提取 JSON 数据

解构赋值对提取 JSON 对象中的数据,尤其有用。

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let { id, status, data: number } = jsonData;

console.log(id, status, number);
// 42, "OK", [867, 5309]
// 上面代码可以快速提取 JSON 数据的值。

# 4.5 函数参数的默认值

jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
} = {}) {
  // ... do stuff
};

指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';这样的语句。

更多的解构场景,基本上都是在上面的基础上略作改动,希望大家能看到它的闪光点哦~

最后提一句,既然到此我们已经了解了解构赋值,不妨在之后的代码中完全使用它!!!

在这里插入图片描述

参考

# 写在最后

JavaScript系列:

  1. 《JavaScript内功进阶系列》(已完结) (opens new window)
  2. 《JavaScript专项系列》(持续更新) (opens new window)
  3. 《ES6基础系列》(持续更新) (opens new window)

关于我

  • 花名:余光(沉迷JS,虚心学习中)
  • WX:j565017805

其他沉淀

如果您看到了最后,对文章有任何建议,都可以在评论区留言

这是文章所在GitHub仓库的传送门 (opens new window),如果真的对您有所帮助,希望可以点个star,这是对我最大的鼓励 ~

Last Updated: 2/15/2023, 7:45:22 PM