踩坑指南:JavaScript解构赋值

·  阅读 3611
踩坑指南:JavaScript解构赋值

踩坑指南:JavaScript解构赋值

开篇

自从 2015 年ES6发布之后,到现在在项目中使用ES6语法应该是基本的操作,比如:const let class => 等等,就在今天我在使用 ES6 的解构赋值的时候翻车了...

以下是翻车现场

在项目开发中我经常使用解构赋值来获取 Object 的属性或者 Array的值

# 要解构属性的对象
const result = { a: 'a', b: 'b' }
# 使用解构来获取 a 和 b
const { a, b } = result
复制代码
# 要解构的数组
const list = [1, 2, 3]
const [item, ...rest] = list
复制代码

然而在项目中解构null 属性的对象的时候翻车了

# 后台返回的数据时这样的
{
  username: '',
  identity: 'admin',
  avatar: null,
  token: 'f0a74a994e84e8ed3be11ed9f4bb5417'
}
复制代码

然后在解构数据的时候为 avatarusername 设置了默认值

const { username = '默认用户', identity = 'ADMIN', avatar = 'https://unavatar.now.sh/github/hom' } = result
复制代码

在接下来使用avatar 的时候,发现值为 nullavatar没有赋值为默认的属性,导致后面的vuex赋值报错。而当我将赋值语句更改为ES5的赋值默认值语法的时候运行正常

const avatar = result.avatar || 'https://unavatar.now.sh/github/hom'
复制代码

然后我就去查看 MDN关于解构赋值的文档,大概知道了为什么。这里也建议如果开发中对Javascript或者CSS有疑惑的地方,首选应该去查看MDN的文档

什么是解构赋值

解构赋值语法是一种 Javascript 表达式。通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。

JavaScript开发中我们会频繁使用 objectarray的值,按照以往使用其值得方式是通过.或者[]来使用,而在 ES6中有新的方式去使用,就是解构赋值。

基本使用

常用的使用方式就是以字面量[]或者{}来获取 array或者 object的值

数组解构赋值

let a, b, rest;
[a, b] = [10, 20];

console.log(a);
// expected output: 10

console.log(b);
// expected output: 20

[a, b, ...rest] = [10, 20, 30, 40, 50];

console.log(rest);
// expected output: Array [30,40,50]
复制代码

对象解构赋值

({ a, b } = { a: 10, b: 20 });
console.log(a); // 10
console.log(b); // 20

({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}
复制代码

常规用法

使用解构赋值经常使用到默认值的赋值,和使用 function的默认参数一样,使用 = 来添加属性的默认值

let a, b;

[a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7
复制代码
let {a = 10, b = 5} = {a: 3};

console.log(a); // 3
console.log(b); // 5
复制代码

问题

本篇文章出现的原因就是在使用解构赋值绑定默认值的时候出现了预期之外的结果。我们在解构源对象不存在的属性设置默认值得时候是没有问题的,但是在解构源对象已有属性,属性的值决定了我们设置默认值是否成功

现象

const { a = '1', b = '2' } = { a: 'a' }
# output:
# a = 'a'
# b = '2'

const { a = '1', b = '2' } = { a: 'a', b: null }
# output:
# a = 'a'
# b = null

const { a = '1', b = '2', c = '3', d = '4', e = '5' } = let object = { a: 'a', b: null, c: undefined, d: false, e: '' }
# output:
# a = 'a'
# b = null
# c = '3'
# d = false
# d = ''
复制代码

我们可以看到只有值在 undefined 的时候默认赋值才能够赋值成功,而当我们使用 ES5 的语法来绑定默认值和解构赋值的结果有些不一样

const result = { a: 'a', b: null, c: undefined, d: false, e: '' }

# a
let a = result.a || '1'
# output: a = 'a'

# b
let b = result.b || '2'
# output: b = '2'

# c
let c = result.c || '3'
# output: c = '3'

# d
let d = result.d || '4'
# output: d = '4'

# e
let e = result.e || '5'
# output: e = '5'
复制代码

是不是有一些意外,使用 ES5 方式的默认赋值和解构赋值的结果不一样,ES5 赋值的属性值为null undefined false '' 的时候均能够赋值默认值

思考

在看到赋值的结果的时候我们需要思考一下为什么会出现这样的结果,3 分钟......

解构赋值

MDN的文档中我们看到这样一句话

为了防止从数组中取出一个值为undefined的对象,可以在表达式左边的数组中为任意对象预设默认值。

对象的默认值绑定是在 array 的基础中添加的功能。什么时候会出现值为undefined 的属性名呢?答案是没有定义这个变量或者变量的值设置为 undefined的时候,而null '' false 在变量解析的过程中都是有值的情况,所以我们默认赋值会不成功,这儿可以参考一下函数的默认值

function (a, b = '2') {
  // function content
}
复制代码

使用函数的默认值得时候,只要在当前的函数位置有参数,默认值解析式就会跳过默认值赋值,所以不管你是null还是'',都按照原始格式传入函数中。

所以我们在之后的开发中,如果只是根据属性的存在与否来绑定默认值的时候,可以使用解构赋值

使用||赋值

ES5 中的默认赋值我们会使用要判断的属性名加上||再加上默认值来绑定

const a = result.a || '1'
复制代码

也正是由于使用了 || 来判断对象值得存在与否,所以只要对象的属性值能够转换为booleanfalse,默认值绑定就会成功,所以上文中我们使用的null undefined false ''都可以赋值成功。

如果使用||来绑定默认值的时候,同样也需要注意一点,由于||是通过将值转换为truefalse来进行短路操作,所以如果对象的值本身就是false的时候,如果你的默认赋值是其他类型的值,比如我在实例中使用的'1'false或者是默认值为true

const result = { a: false }
# 下面是错误的示例
const is = result.a || true
复制代码

在上面的示例中是否发现了什么问题?对,就是is的值无论什么时候都是true,而忽略了result的原始值,这一点是我们需要注意的,可能会导致一些不可复现的 bug

总结

在上面的讨论之后,我们发现之后再使用默认值绑定,可以通过解构赋值来确定属性值是否存在为undefined,随后在程序中使用||来判断是否需要对逻辑值为false的属性进行默认值绑定

const result = { a: '' }

let { a, b = 'default b' } = result

let a = a || 'default a'
复制代码

使用示例代码所使用的方法我们就可以对我们想要绑定默认值得属性进行正确的操作了。

参考

  • MDN解构赋值的语法

更多内容

更多的内容请关注GitHub,知乎@如小非,或者关注我的公众号@全栈开发师,我会不定时分享一些全栈方向的开发内容,谢谢分享

本文使用 mdnice 排版

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改