JavaScript 善用解构赋值

本文最后更新于:2020年12月25日 晚上

场景

在今天写 JavaScript 函数时,发现了一个有趣的技巧。

在此之前,吾辈想知道泥萌需要默认值的时候是如何做的呢?

例如下面的函数 print,吾辈需要在没有给定参数 user 的情况下,给出合适的输出

1
2
3
4
5
6
7
8
9
10
11
12
function print(user) {
if (!user) {
user = {};
}
if (!user.name) {
user.name = "未设置";
}
if (!user.age) {
user.age = 0;
}
console.log(`姓名:${user.name},年龄:${user.age}`);
}

那么,我们应该怎么优化呢?

  • 三元表达式
  • || / && 赋予默认值
  • Object.assign() 合并对象

我们分别来实现一下

三元表达式实现

1
2
3
4
5
6
7
8
function print(user) {
user = user ? user : {};
console.log(
`姓名:${user.name ? user.name : "未设置"},年龄:${
user.age ? user.age : 0
}`
);
}

|| / && 赋予默认值

1
2
3
4
5
function print(user) {
console.log(
`姓名:${(user || {}).name || "未设置"},年龄:${(user || {}).age || 0}`
);
}

使用 && 也可以

1
2
3
4
5
function print(user) {
console.log(
`姓名:${(user && user.name) || "未设置"},年龄:${(user && user.age) || 0}`
);
}

|| / && 解释

  • || 用来取默认值,避免太多的 if 判断。例如对于 a || b 我们可以认为:如果 a 为空,则赋值为 b
  • && 用来连续执行,避免深层嵌套的 if 判断。例如对于 a || b,我们可以认为:如果 a 不为空,则赋值为 b

注:||/&&` 非常适合简单的默认值赋值,但一旦设置到深层嵌套默认值就不太合适了

Object.assign() 合并对象

1
2
3
4
5
6
7
8
function print(user) {
_user = {
name: "未设置",
age: 0,
};
user = Object.assign(_user, user);
console.log(`姓名:${user.name},年龄:${user.age}`);
}

可以看出

  1. 三元表达式的方式方式明显有点繁琐
  2. || / && 很好很强大,缺点是看起来很不直观,而且容易混淆
  3. Object.assign() 合并对象的方式应该算是最好的了,然而是在方法内部进行的初始化,作为调用者除非查看文档或源码才能知道

那么,有没有更好的解决方案呢?

解构赋值

解构赋值是 ES6 的一个新的语法,具体可以查看 MDN

下面是一些简单的解构赋值操作

数组解构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var arr = [1, 2, 3, 4];
var [first, second, ...remaining] = arr; // first: 1, second: 2, remaining: [3, 4]
// 接受一或多个可变参数的函数
function join(...arr) {
return arr.join(", ");
}
// 调用时可以使用 ... 将数组解构
join(...arr); // 1, 2, 3, 4
// 忽略开始的某些值
var [, , ...remaining] = arr; // remaining: [3, 4]
// 默认值
var [first = 1, second = 2, ...remaining] = []; // first: 1, second: 2, remaining:
var a = 1,
b = 2;
// 交换变量
[a, b] = [b, a]; // a: 2, b: 1

对象解构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var user = {
id: 1,
name: "未设置",
age: 0,
sex: false,
};
// 排除指定属性
var { name, age, ...rest } = user; // name: '未设置', age: 0, 其他属性: { "id": 1,"sex": false }

// 使用新的变量名
var { name: newName, age: newAge } = user; // newName: '未设置', newAge: 0
// 默认值
var { name = "未设置", age = 0 } = {}; // name: '未设置', age: 0
// 同时使用新的变量名和默认值
var { name: newName = "未设置", age: newAge = 0 } = user; // newName: '未设置', newAge: 0
// 计算属性名
var key = "name";
var { [key]: name } = user;

// 数组迭代解构
var users = [
{
name: "琉璃",
age: 17,
},
{
name: "楚轩",
age: 23,
},
];
users.map(({ name, age }) => `name: ${name}, age: ${age}`).join("\n");
// 解构函数实参
function print({ name = "未设置", age = 0 } = {}) {
console.log(`姓名:${name},年龄:${age}`);
}

啊嘞,吾辈好像不知不觉间把解决方案写出来了。。。?

分析

让我们好好看下这段代码

1
2
3
function print({ name = "未设置", age = 0 } = {}) {
console.log(`姓名:${name},年龄:${age}`);
}

一眼看过去,是不是感觉很直观,如果稍微了解一点 ES6 就能瞬间明白这是解构赋值以及默认参数

我们分析一下具体流程

  1. 调用 print 函数

  2. 检查参数是否为有值,没有的话设置默认值 {}
    相当于

    1
    2
    3
    if (!user) {
    user = {};
    }
  3. 解构参数,检查解构的属性是否有值,没有的话设置默认值
    相当于

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var name;
    if (!user.name) {
    name = "未设置";
    } else {
    name = user.name;
    }
    var age;
    if (!user.age) {
    age = 0;
    } else {
    age = user.age;
    }
  4. 进入函数内部

关键就在于第 2,3 步,默认参数解构赋值 都是 ES6 的新特性,善于使用能大大简化代码的繁琐性。


希望有更多的人能够学会怎么使用,让我们早日抛弃 babel 吧 (*^ ▽ ^)/