【问题标题】:How to control property enumeration (for...in) with Proxy objects?如何使用代理对象控制属性枚举(for...in)?
【发布时间】:2016-07-26 09:47:21
【问题描述】:

我将一个对象包装在 Proxy 中,然后遍历它。如何控制它迭代的键?

如果我不覆盖密钥,代理就可以工作:

var obj = {"hello": "world"}
var proxy = new Proxy(obj, {})
for (var key in proxy){
    console.log(key)
}
// logs "Hello"

但是,如果我更改 ownKeys 处理程序中的键,则不会记录任何内容。

var obj = {"hello": "world"}
var proxy = new Proxy(obj, {
    ownKeys: function(){
        return ["a", "b"]
    }
})
for (var key in proxy){
    console.log(key)
}
// Logs nothing

如果我将"hello" 作为ownKeys 的一部分返回,则仅记录"hello"

显然 ES6 中有一个 enumerate 陷阱,但它已从 ES7 中删除。

是否仍然可以使用代理控制for...in 循环?为什么从规范中删除了enumerate

【问题讨论】:

  • 你还需要实现 getOwnPropertyDescriptor 代理陷阱,将它们设置为可枚举!

标签: javascript proxy-object


【解决方案1】:

很遗憾,不能再这样做了。

正如 Brian Terlson(EcmaScript 规范的编辑)所写:

代理枚举陷阱和 for-in 的问题,其中 iimplementations 是 阻止预填充对象中的键列表,因为 迭代器导致可观察到的影响。这意味着迭代必须 每次迭代都被拉取。上次会议我们认为没问题 如果枚举陷阱耗尽了迭代器,我们认为这会 解决这个问题。问题是,现在它们是可观察的 对象和该对象的代理之间的区别,主要是由于 删除。

(来源:https://github.com/rwaldron/tc39-notes/blob/master/es7/2016-01/2016-01-28.md#5xix-proxy-enumerate---revisit-decision-to-exhaust-iterator 通过https://ecmascript-daily.github.io/2016/02/10/why-remove-enumerate-and-reflect-enumerate

因此,由于无法以令人满意的方式解决技术挑战,因此将其删除。

代理陷阱

in 这样的运算符仍然可以使用has proxy trap 捕获:

var p = new Proxy({}, {
  has: function(target, prop) {
    if (prop === 'a') { return true; }
    return false;
  }
});
'a' in p; // true
'b' in p; // false

替代方案

由于如今for (let key in proxy) 循环更像是一项遗留功能,您可以将以下选项之一与ownKeys 代理陷阱一起使用:

  • Object.keys()(仅限自己的可枚举属性)
  • Object.getOwnPropertyNames()(自有财产)
  • Reflect.ownKeys()(自己的属性和符号)

(来源:https://twitter.com/nilssolanki/status/659839340592422912

(但您可能已经知道这一点,因为您首先使用的是代理)

【讨论】:

  • 谢谢尼尔斯,很棒的答案。我碰巧处于一个可以在执行前使用 Babel 修改代码的环境中,所以我可以通过这种方式添加我的自定义逻辑。
  • 我可以在执行前使用 Babel 修改代码,所以我可以通过这种方式添加我的自定义逻辑。我不完全确定你的意思。你能详细说明一下吗?
  • 基本上我可以在代码运行之前对其进行转换,所以我可以用其他东西替换for...in 循环,或者我可以更新它以包含我的逻辑。在大多数生产情况下这不是你可以做的,但我已经在我正在使用的代码上运行 Babel,所以这不是问题。
  • for (key in proxy) 可以实现。正如 Nils 所说,您需要实现 ownKeys 代理陷阱,也许您也需要实现 getOwnPropertyDescriptor 代理陷阱以将它们设置为可枚举!
【解决方案2】:

用户 user2106769 的建议和 yeerk 的覆盖 getOwnPropertyDescriptor 以允许枚举代理属性的答案存在一个缺陷,使用它时应该注意,它在捕获 getOwnPropertyDescriptor 时没有设置 value 属性,因此其他一些代码依赖关于该行为将无法正常运行。

演示该缺陷的代码及其解决方案如下:

var obj = { "hello": "world" };
var flawedProxy = new Proxy(obj, {
    ownKeys: function() {
        return ["a", "b"];
    },
    getOwnPropertyDescriptor: function(target, key) {
         return { enumerable: true, configurable: true };
    }
});

var goodProxy = new Proxy(obj, {
    get: function(target, key) {
      // modify something here if you want to
      return target[key];
    },
    ownKeys: function() {
        return ["a", "b"];
    },
    getOwnPropertyDescriptor: function(target, key) {
         return { value: this.get(target, key), enumerable: true, configurable: true };
    }
});

// value is accessible, getOwnPropertyDescriptor not trapped
console.log(Object.getOwnPropertyDescriptor(obj, 'hello').value);

// value is undefined, getOwnPropertyDescriptor not trapped correctly
console.log(Object.getOwnPropertyDescriptor(flawedProxy, 'hello').value);

// value is accessible, getOwnPropertyDescriptor trapped correctly
console.log(Object.getOwnPropertyDescriptor(goodProxy, 'hello').value);

【讨论】:

  • 这不是陷阱工作原理的错误吗?似乎应该设置 .value ..
  • 与生产中所需的相比,这似乎仍然过于简化。在某些情况下,可枚举和可配置不需要为假,而只是将整个对象覆盖为真可能会导致问题?
【解决方案3】:

user2106769 将解决方案作为评论提供,但对于像我这样没有看到他们评论的其他人,您可以使用 ownKeysgetOwnPropertyDescriptor 覆盖 for..in 迭代:

var obj = { "hello": "world" };
var proxy = new Proxy(obj, {
    ownKeys: function() {
        return ["a", "b"];
    },
    getOwnPropertyDescriptor: function(target, key) {
        return { enumerable: true, configurable: true, value: this[key] };
    }
});
for (var key in proxy) {
    console.log(key);
}

编辑:正如 Pärt Johanson 所说,为了正确起见,getOwnPropertyDescriptor 应返回“值”。

【讨论】:

    猜你喜欢
    • 2010-09-10
    • 2012-05-02
    • 2017-03-05
    • 1970-01-01
    • 1970-01-01
    • 2017-07-12
    • 2022-09-23
    相关资源
    最近更新 更多