The enumerate trap and Object.create()

# Leon Arnott (10 years ago)

Lately I've been puzzled about what the Proxy "enumerate" trap is supposed to accomplish w/r/t enumeration over prototype properties. Consider the following:

  let obj = {"actual-property":1};

  let proxy = new Proxy(obj, {*enumerate(){
    yield "falsified-property";
  }});

The implication of the above is that all for-in loops run on the proxy will always produce "falsified-property" and never "actual-property":

  for (i in proxy) {
    console.log(i); // Logs "falsified-property"
  }

However, this proxy behaviour can be easily bypassed by just adding Object.create():

  for (i in Object.create(proxy)) {
    console.log(i); // Logs "actual-property"
  }

When for-in is performed on an inheritor of a proxy, the proxy's "enumerate" trap is never hit at all. This seems undesirable - I think it's intuitive that since enumeration is a prototype-traversing operation, it should also respect prototypes' traps. And, of course, that adding an empty ordinary object to the head of the prototype chain shouldn't dramatically alter the results of a for-in performed on that chain.

As far as I know, this enumerate trap prototype behaviour is unique among traps: the other three traps that explicitly deal with prototype properties

  • "get", "set" and "has" - are specced to have their traps triggered by operations performed on ordinary object inheritors of them.

The reason for this is, of course, due to how Ordinary Object [[Enumerate]] is currently specced, compared to Ordinary Object [[Get]], [[Set]] and [[Has]]. [[Enumerate]] is required to process properties from prototypes of the object, but is not required to call the prototype's [[Enumerate]] as the means of accessing them. (In fact, the informative algorithm provided calls the prototype's [[OwnPropertyKeys]] instead.)

That being said, I want to ask:

  • Is there a reason or requirement that enumerate traps only trigger when for-in operations are performed on the proxy directly?
  • Could Ordinary Object [[Enumerate]] be changed to, at the least, require prototypes' [[Enumerate]] be used to retrieve their properties, rather than any method that would ignore it?

Thanks.

# Allen Wirfs-Brock (10 years ago)

Thanks, Please file a bug on this at bug.ecmascript.org

# Brendan Eich (10 years ago)

Good catch! I think this old decision pre-dates for-of. Cc'ing Tom.

Leon, could you please cite the bug in a followup here if you file it? Thanks,

# Gary Guo (10 years ago)

Seems a serious problem, but could be easily fixed (I think).

For Example, this could be used as [[Enumerate]] for ordinary objects:

9.1.11 [Enumerate]

When the [[Enumerate]] internal method of O and optional argument visitedSet is called the following steps are taken

Return an Iterator object, whose next method conforms to the following steps.

If visitedSet does not exist, let visitedSet be an empty set

Let allKeys be a list that contain of all property names of O

For each property key in allKeys

If visitedSet does not contains key and key is an enumerable property of O

Add key to visitedSet

Yield key

Let proto be the result of calling [[GetPrototypeOf]] internal method of O

Let protoIterator be the result of calling [[Enumerate]] internal method of proto

Yield* protoIterator

# Leon Arnott (10 years ago)

The bug has been filed: ecmascript#3724 Thanks.