Matt Zeunert Matt Zeunert - 4 months ago 10
Javascript Question

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

I'm wrapping an object in a Proxy and then iterate through it. How can I control the keys it iterates through?

The proxy works if I don't override the keys:

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


However, nothing is logged if I change the keys in the
ownKeys
handler.

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


If I return
"hello"
as part of the
ownKeys
only
"hello"
is logged.

Apparently there was an
enumerate
trap in ES6, but it has been removed from ES7.

Is it still possible to control the
for...in
loop with a Proxy? Why was
enumerate
removed from the spec?

Answer

Unfortunately, it isn't possible to do this anymore.

As Brian Terlson (the editor of the EcmaScript Specification) wrote:

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.

(Source: 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)

So it was removed due to technical challenges that could not be solved in a satisfactory manner.

has proxy trap

The in operator as such can still be captured using the 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

Alternative

As for (let key in proxy) loops are more of a legacy feature these days, you could use one of the following with the ownKeys proxy trap:

  • Object.keys() (own enumerable properties only)
  • Object.getOwnPropertyNames() (own properties)
  • Reflect.ownKeys() (own properties and Symbols)

enter image description here (Source: https://twitter.com/nilssolanki/status/659839340592422912)

(but you probably already knew that, seeing that you are working with proxies in the first place)