【问题标题】:Using Proxies with plain arrays将代理与普通数组一起使用
【发布时间】:2016-04-22 06:04:37
【问题描述】:

考虑这段代码:

app.mediaLibrary = new function MediaLibrary() {
    var self = this;

    self.media = new Proxy([], {
        set(target, key, value) {
            console.log(`Setting value ${key} as ${value}`)
            if (!(value instanceof self.Media))
                throw new Error(`${value} is not instance of Media.`)
            target[key] = value;
            return true
        },
        get (target, key) {
            console.log(`Getting value ${key} as ${target[key]}`)
            return target[key]
        }
    });

    self.Media = function Media(file, fileReader) {
        this.src = fileReader.result;
        this.file = file;
    }

    return self;
}

每当我打电话给app.mediaLibrary.media.push(new app.mediaLibrary.Media("", "")) 在控制台中我看到了这个:

Getting value push as function push() { [native code] }
Getting value length as 0
Setting value 0 as [object Object]
Setting value length as 1
Uncaught Error: 1 is not instance of Media.(…)

我明白为什么我会看到这个,但我该如何围绕它编写代码?看来我的陷阱是由内部(push,length)和外部调用([0]=...)触发的,我不知道如何区分它们。有什么想法吗?

【问题讨论】:

  • 我不明白你的差异化方案。如果一个电话可以被视为“外部”,那么肯定是push。它依次调用“get length”和“set 0”。那么究竟是什么将属性的访问限定为内部的呢?
  • 我没有在我的代码中调用 get length,所以它是内部/隐式调用,而不是外部/显式调用。
  • 根据该定义,世界上的每个库都只包含内部调用。即使有人在回调中调用您的代码。这没有多大意义。
  • 是的,你明白了。在我的情况下,该库是数组的香草实现。无论如何,你知道我该如何解决我的问题吗?
  • “我怎样才能解决我的问题” 我对你想要达到的目标有一个模糊的概念 - 由于显而易见的原因这是不可能的 - 但老实说我没有看到需要解决的问题。

标签: javascript ecmascript-6 es6-proxy


【解决方案1】:

我认为你问错了问题。这不是关于内部调用与外部调用,而是关于您正在代理的特定对象:一个数组。

你可以满足三个条件:

  1. 长度可以设置为任何值(无论如何只有数组会处理)
  2. 数字属性只能设置为媒体实例。
  3. 所有其他属性均禁止使用。

你可以这样写:

app.mediaLibrary = new function MediaLibrary() {
    var self = this;

    self.media = new Proxy([], {
        set(target, key, value) {
            console.log(`Setting value ${key} as ${value}`)

            // Check if this is a valid array index
            // See http://www.ecma-international.org/ecma-262/6.0/#sec-array-exotic-objects
            if (String(key >>> 0) === key && (key >>> 0) != Math.pow(2, 32) - 1) {
                if (!(value instanceof self.Media))
                    throw new Error(`${value} is not instance of Media.`);
            } else if(key !== 'length') {
                throw new Error(`${key} may not be written to.`);
            }
            target[key] = value;
            return true
        },
        get (target, key) {
            console.log(`Getting value ${key} as ${target[key]}`)
            return target[key]
        }
    });

    self.Media = function Media(file, fileReader) {
        this.src = fileReader.result;
        this.file = file;
    }

    return self;
}

如果你真的需要区分.push[0]=...,那么不,做不到。

【讨论】:

  • 好吧,您可以通过在 getter 中装饰返回的函数来区分 .push(),但我同意像您一样检查属性名称时没有必要。
  • 您可能希望明确检查String(key >>> 0) === key && (key >>> 0) != 4294967295,而不是使用Number(key) == key
  • @Bergi 装饰师的好主意,谢谢。我对按位运算符有点参差不齐,您介意解释一下您的第二部分吗(>>> 似乎做了类型转换和舍入技巧)? 4294967295 是可能的最高索引吗?
  • 从字面上看是规范中的isArrayIndex algorithm :-) >>> 0 是常见的ToUint32 复制。
  • 非常感谢,这很有道理。我将它整合到答案中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-20
  • 2018-07-03
  • 2013-01-10
  • 2018-11-02
  • 2014-03-06
  • 2016-12-09
  • 2013-06-11
相关资源
最近更新 更多