在Proxy get处理程序中区分直接访问和内部访问

时间:2016-08-12 12:45:08

标签: javascript ecmascript-6 es6-proxy

我有一个数组:

const arr = ['a', 'b', 'c']

我使用get处理程序为该数组创建代理,该处理程序将为每个数字n - 1返回带有n索引的属性。例如,p[1]将返回'a'p[2] - 'b'p[3] - 'c'

const p = new Proxy(arr, {
  get: (target, property, receiver) => {
    const parsed = parseInt(property, 10)
    if (!Number.isNaN(parsed)) return target[parsed - 1]
    return target[property]
  }
})

似乎工作正常。例如,p[2]可以提供'b'。但是,还有另一个问题。 Array.prototype.indexOf()无法正常工作。当我通过'a''b'时它会起作用 - 它分别返回12,但对于'c',它会返回-1。事实证明,indexOf()也会触发get处理程序。通过将console.log(property)添加到get处理程序,我们可以获得p.indexOf('c')的以下输出:

'indexOf'
'length'
'0'
'1'
'2'

似乎indexOf()在内部检查length属性,然后将数组从索引0迭代到length - 1

如果我知道该属性是直接访问(如p[2])还是内部访问,那么解决这个问题将是微不足道的。然后我总是可以返回target[property]进行内部访问(因此代理将是no-op)。

如何区分代理get处理程序中的直接访问与内部访问?

我唯一想到的就是抛出一个错误,抓住它并分析它的堆栈。尽管如此,这似乎是一种解决方法而不是实际的解决方案,所以我想避免使用它,除非别无其他。

2 个答案:

答案 0 :(得分:1)

  

似乎indexOf()在内部检查length属性,然后将数组从索引0迭代到length - 1

是。这是所有内置数组方法的行为。

  

如何在Proxy get handler中区分直接访问和内部访问?

你不能。

  

如果我知道该属性是直接访问还是内部访问,那么修复它将是微不足道的。

不,这是错误的做法。如果您希望forEach的行为与普通循环不同,那么您的语义就会出现问题。也许你真的想截取.length属性?或者包裹索引0?在不知道你试图解决的问题的情况下,我们几乎无法提出任何有用的建议。

一个极端的措施是编写自己的所有数组方法版本来处理单索引数组。

答案 1 :(得分:1)

正如torazaburo在评论中所建议的,一种可能的解决方案是为indexOf处理程序中的get属性返回不同的函数,并在那里进行必要的操作。具体来说,我们可以使indexOf()方法适用于原始数组,而不是代理;并将1添加到结果中。

if (property === 'indexOf') {
  return (...args) => {
    const result = Reflect.apply(target.indexOf, target, args)
    return result === -1 ? result : result + 1
  }
}

工作代码段:

const arr = ['a', 'b', 'c']
const p = new Proxy(arr, {
  get: function(target, property, receiver) {
    if (property === 'indexOf') {
      return (...args) => {
        const result = Reflect.apply(target.indexOf, target, args)
        return result === -1 ? result : result + 1
      }
    }
    const parsed = parseInt(property, 10)
    if (!Number.isNaN(parsed)) return target[parsed - 1]
    return target[property]
  }
})
console.log(p.indexOf('c'))

其他数组方法可以类似地修复。