最近遇到了一个问题,来自于下面的一段代码:
let { [key]: id, ...rest } = obj
在这篇文章里,我想解释下它是在做什么以及它是如何工作的。
提醒:因为它非常的晦涩难懂,所以我最终并没有以这种方式实现去做,不过它是非常有趣的,值得去我们去了解。
如何遇到这个问题
假如我们有以下数组
const users = [
{ name: 'Michael', group: 1 },
{ name: 'Lukas', group: 1 },
{ name: 'Travis', group: 2 },
]
我们把它按照 group
字段进行分组映射如下:
{
'1': [
{ name: 'Michael' },
{ name: 'Lukas' },
],
'2': [
{ name: 'Travis' },
]
}
如何从 users
对象中删除 group
属性,实现上述效果?
我们可以利用如下方法:
users.reduce((result, user) => {
const { group, ...userData } = user
result[group] = result[group] || []
result[group].push(userData)
return result
}, {})
这里用到了reduce
函数,如果不熟悉的同学,可以去查看相关资料就不多说了。
我的最终目标是使这个函数具有动态性,而现在是通过固定字段 group
来分组,并不是计算得来的。假如以后想使用其他字段进行分组就需要更改函数了。
在实现动态性之前我们先看看
const { group, ...userData } = user
因为它也是这篇文章我们想谈论的知识。
解构
上面的数据中每个用户都有 group
和 name
属性。因此在 ES6 中可以使用解构的方式获取对象中的值。
例如:
const { group } = user
它等效于
const group = user.group
还可以这么做
const { group, name } = user
它等效于
const group = user.group
const name = user.name
剩余参数
const { group, ...userData } = user
现在,这段代码有一个更复杂的问题需要我们讨论。
...userData
获取了除 group
之外的所有值,并把它们浅拷贝到一个新的常量 userData
中。在这种情况下 userData
变成一个仅有 name
属性的对象。
userData = {
name: "xx"
}
在这里,我们不要混淆剩余参数和扩展运算,它们其实刚好是相反的。
const location = { country: 'Japan', city: 'Tokyo' }
const newLocation = { ...location, zipcode: 123456 }
//{country: "Japan", city: "Tokyo", zipcode: 123456}
这里将会把 location
对象的属性全部展开,然后放入 newLocation
对象中。此时的 newLocation
对象将包含如下属性:
{country: "Japan", city: "Tokyo", zipcode: 123456}
那么什么时候是「剩余参数」,什么时候是扩展运算?这将取决于赋值在哪边,在赋值左边的就是剩余参数,在赋值右边的就是扩展运算。
你也可以在函数中使用剩余参数
class BaseArray extends Array {
constructor(...values) { // rest
super(...values) // spread
}
}
此时让我们来看看实现函数动态性的解决方案:
function groupBy(array, key) {
return array.reduce((result, item) => {
const { [key]: id, ...rest } = item
result[id] = result[id] || []
result[id].push(rest);
return result;
}, {})
}
现在到底什么是 const { [key]: id, ...rest } = item
?
我们已经知道 ...rest
意味着什么了。所以我们就不说了。在解释 [key]: id
之前,我们来看一个简单的例子。
分配新变量名
还记得这个吗?
const user = { group: 1 }
const { group } = user
console.log(group) //1
如果我们将 group 的值去转换为一个变量名为发生什么?我们可以这么做
const user = { group: 1 }
const { group: id } = user
console.log(id) //1
此时将会把 group 的值赋值给变量 id。
这实际上是非常有用的,因为有些时候对象的 key
作为变量名是无效的。
例如:
const foo = { 'fizz-buzz': true }
const { 'fizz-buzz' } = foo
此时程序就会报错, 因为 fizz-buzz
不可以当作变量名使用。正确的写法如下:
const { 'fizz-buzz': fizzBuzz } = foo
那么我们该如何记住这个语法呢?其实是很简单的,这和我们创建对象时使用的是完全相同的语法。
const id = 1
const user = {
group: id
}
因此,如果对象是在赋值(=)的右边,group 属性保存变量 id。
如果它是在赋值(=)的左边,它刚好是相反的。
const { group: id } = user
我们获取属性 group 的值,并将其放入变量 id 中。
最后,计算对象属性名
其他的都说完了,现在唯一解释的就剩下 [key].
了。
我们可以使用它来访问计算属性名,在我们的例子中变量 key 的值是 group。
创建对象时如何添加计算 keys ?
使用相同的语法,只是它在赋值(=)的右边。
const key = 'group'
const id = 1
const user = {
[key]: id
}
但是如果我们只写 let { [key] } = obj
那么我们应该用什么名字来访问这个变量呢?我们是不能这样的。
因此,就像 fizz-buzz
一样,我们最终的方式就是:[key]: id
。
所以就是这样,我们还可以设置默认值应用于 id。
通常会是这样的
const user = { group: 1 }
const { group = 0, createdAt = null} = user
使用计算属性,它变成
let { [key]: id = 0, ...rest } = obj
原文:https://dev.to/mzanggl/let--key-id--0-rest---obj---destructuring-to-the-limit-deo
翻译:六小登登
更多优质文章:六小登登的博客
我是:六小登登,一名爱写作的技术人,从零开始自学前端,经常分享原创干货。
关注公众号:六小登登,后台回复「1024」即可免费获取惊喜福利!后台回复「加群」群里每天都会全网搜罗好文章给你。