【问题标题】:Distinguish direct access from internal access in Proxy get handler在代理获取处理程序中区分直接访问和内部访问
【发布时间】:2016-08-12 12:45:08
【问题描述】:

我有一个数组:

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

我使用get 处理程序为该数组创建一个代理,该处理程序将为每个数字n 返回具有n - 1 索引的属性。例如,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] 进行内部访问(因此代理将是无操作的)。

Proxygethandler中如何区分直接访问和内部访问?

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

【问题讨论】:

  • 为什么要采用这些非一的语义?怀念一些基于 1 的语言?
  • @torazaburo 我只是在试验一下,看看是否可以创建一个索引从 1 开始的数组,而不是 0。
  • 我认为,但不确定,您必须为 indexOf 方法在对象上实现 get 并进行必要的操作。
  • @torazaburo 根据你的建议,我想出了this。你介意我用这个解决方案自行回答问题吗?

标签: javascript ecmascript-6 es6-proxy


【解决方案1】:

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

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

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

你不能。

如果我知道该属性是直接访问还是内部访问,那么解决这个问题就很简单了。

不,这是错误的方法。如果您期望 forEach 的行为与正常循环不同,那么您的语义就会出现问题。也许您实际上也想拦截 .length 属性?还是环绕索引0?在不知道您要解决的问题的情况下,我们几乎无法提出任何有用的建议。

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

【讨论】:

  • 重写所有数组方法有一个很大的缺点(除了必须编写它们之外):这些方法会比原生方法慢。
  • 好吧,本机方法适用于从零开始的数组,所以你不能吃蛋糕然后吃掉它。顺便说一句,他们可能不会那么慢。
  • 好吧,我想我可以。见my answer
  • @Gothdo:嗯,这本质上是你自己的indexOf 版本(而且可能也很慢,因为闭包使它绑定到arr)。 p.indexOf !== p.indexOf 也很奇怪。
【解决方案2】:

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

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

工作代码sn-p:

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'))

其他数组方法可以类推固定。

【讨论】:

  • 哦,终于if(p.indexOf(…)) 像大多数人期望的那样工作了:-)
  • @Bergi 我忘了。已更正。
猜你喜欢
  • 1970-01-01
  • 2019-05-26
  • 1970-01-01
  • 2015-03-27
  • 2019-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多