阅读 344

let { [key]: id = 0, ...rest } = obj-极限解构

最近遇到了一个问题,来自于下面的一段代码:

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
复制代码

因为它也是这篇文章我们想谈论的知识。

解构

上面的数据中每个用户都有 groupname 属性。因此在 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
复制代码

原文:dev.to/mzanggl/let…
翻译:六小登登
更多优质文章:六小登登的博客

我是:六小登登,一名爱写作的技术人,从零开始自学前端,常活跃CSDN、掘金、公众号等分享原创干货。 关注公众号:六小登登,后台回复「1024」即可免费获取惊喜福利!后台回复「加群」群里每天都会全网搜罗好文章给你。

关注下面的标签,发现更多相似文章
评论