JavaScript Proxy which returns 默认为空数组

JavaScript Proxy which returns an empty array by default

我想定义一些对象,以便每个 属性 在未定义时提供一个空数组。

示例:考虑用户列表,每个用户都是 {id: number, age: string, ...}。我们想按年龄对用户进行分组:

const users = [{id: 1, age: 42}, {id: 2, age: 42}, {id: 3, age: 43}]

const usersByAge = users.reduce((out, user) => {
  if (!out.hasOwnProperty(user.age)) {
    out[user.age] = []
  }
  out[user.age].push(user)
  return out
}, {})

console.log(usersByAge[42]) -> [{id: 1, age: 42}, {id: 2, age: 42}]
console.log(usersByAge[43]) -> [{id: 3, age: 43}]
console.log(usersByAge[18]) -> undefined
console.log(Object.keys(usersByAge)) -> ['42', '43']

此代码有效,但我想通过使用特殊的 JavaScript 代理来缩短它。如果未定义 属性,它应该 return 一个空数组。即使未定义,也应该可以将项目推入 属性 。它应该为不同的未定义属性提供相同的数组:

const getMapFromIdToArray = () => {
  return new Proxy({}, {
    get: (target, name) => {
      if (!target.hasOwnProperty(name)) {
        target[name] = []
      }
      return target[name]
    }
  })
}

const usersByAge = users.reduce((out, user) => {
  out[user.age].push(user) // <- this should work, array initialization should not be required
  return out
}, getMapFromIdToArray())

console.log(usersByAge[42]) -> [{id: 1, age: 42}, {id: 2, age: 42}] - OK
console.log(usersByAge[18]) -> [] - OK
console.log(usersByAge[18] === usersByAge[19]) -> true - FAIL
console.log(Object.keys(usersByAge)) -> ['42', '43'] - FAIL

此代码部分有效。如果未定义 属性,则将其初始化为 []。所以 getter 实际上改变了目标。我想避免这种情况。但与此同时,我希望能够在未初始化的情况下调用 .push 未定义的 属性 。有可能实现吗?

It should provide the same array for different undefined properties

我不确定你在这句话中的意思,但你肯定不希望两个不同的属性 return 引用同一个数组,然后将属于不同属性的项目推送到该数组.

If the property is not defined, it's initialized as []. So the getter actually mutates the target. I would like to avoid this. But the same time I want to be able to call .push on an undefined property without initialization. Is it possible to achieve?

我会说是和不是。 push 方法必须在实际数组上工作,并且对象必须持有对该数组的引用,以便稍后检索推送的项目。但是如果你不希望空数组立即在目标对象中创建一个实际的属性,那么你可以将它单独存储在代理中,直到它被变异后请求:

new Proxy({}, {
  tempProps: {},
  get: (target, name, reciever) => {
    if (target.hasOwnProperty(name)) {
      return target[name];
    }

    if (tempProps.hasOwnProperty(name)) {
      if tempProps[name].length {
        // array is not empty - bring into target
        target[name] = tempProps[name];
        delete tempProps[name];
        return target[name];
      } else {
        return tempProps[name];
      }
    }

    tempProps[name] = [];
    return tempProps[name];
  }
})

也可以在 WeakRef 对象中保存对 returned 空数组的引用,这样如果调用者删除对该数组的所有引用,它最终将被垃圾回收.但这似乎不必要地复杂,除非在非常特殊的情况下。