如何使用 Proxy 对象控制 属性 枚举(for...in)?

How to control property enumeration (for...in) with Proxy objects?

我将对象包装在 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

如果我 return "hello" 作为 ownKeys 的一部分,只会记录 "hello"

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

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

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

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

issue with proxy enumerate trap and for-in, where iimplementations are prevented from pre-populating the list of keys in the object, because the iterator causes observable affects. Which means the iterate must be pulled for every iteration. Last meeting we thought it would be ok if the enumerate trap exhausts the iterator, we thought that would solve the problem. The issue was, now their is an observable difference between an object and proxy of that object, mainly due to delete.

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

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

代理陷阱

仍然可以使用 has proxy trap:

捕获 in 运算符本身
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

(但您可能已经知道,首先看到您正在使用代理)

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 应该返回“值”。

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

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

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