13

A for-in loop will go through all enumerable properties of an object, even those in the prototype chain. The function hasOwnProperty can filter out those enumerable properties that are in the prototype chain. Finally, the function propertyIsEnumerable can discriminate the enumerable properties of an object.

Therefore, the following script should not print anything:

for(a in window)
    if(window.hasOwnProperty(a) && !window.propertyIsEnumerable(a))
        console.log(a);

On Chrome, however, the above prints a lot of property names.

Why do the for-in loop and propertyIsEnumerable contradict each other regarding enumerables?

Randomblue
  • 112,777
  • 145
  • 353
  • 547
  • 1
    Zero properties printed for me on Firefox 14 ([fiddle](http://jsfiddle.net/SGWj7/)). – Frédéric Hamidi Aug 27 '12 at 17:23
  • 2
    Remember that `window` is **host** variable and does not have to follow the specification exactly. – Felix Kling Aug 27 '12 at 17:24
  • @Felix, do you mean the enumerated object itself chooses how to filter its properties when `for ... in` is applied to it? – Frédéric Hamidi Aug 27 '12 at 17:26
  • @FelixKling: Hum. Can you point to the part of the specification for that? :) – Randomblue Aug 27 '12 at 17:26
  • 2
    Yes, this is weird. `window.propertyIsEnumerable('Int8Array')` is false, but `Object.getOwnPropertyDescriptor(window, 'Int8Array').enumerable` is true. According to the specs, they should be equal. – pimvdb Aug 27 '12 at 17:29
  • http://es5.github.com/#x4.3.8 host objects do not necessarily follow the spec. – Christoph Aug 27 '12 at 17:30
  • 2
    The host object thing is [allowed](http://es5.github.com/#x8.6.2), though: "Host objects may support these internal properties with any implementation-dependent behaviour" - that is for `[[GetOwnProperty]]` which is the descriptor containing `enumerable`. – pimvdb Aug 27 '12 at 17:32
  • 3
    For example: *"Host objects may implement these internal methods in any manner unless specified otherwise; for example, one possibility is that [[Get]] and [[Put]] for a particular host object indeed fetch and store property values but [[HasProperty]] always generates false."* http://es5.github.com/#x8.6.2. I'm not saying that is the case here, it might be indeed a bug, but it has to be kept in mind. – Felix Kling Aug 27 '12 at 17:37
  • The global object is a *standard built-in* object, **not** a *host object*. Its properties may well be, but that should not affect `for-in` loops. – user123444555621 Sep 15 '12 at 06:39

2 Answers2

3

The sad fact is that JavaScript engines are not the same. While ES5 conforming engines will dutifully respect the enumerability of their properties, window is a host object, as pointed out by pimvdb and Felix above, and not beholden to the rules of ES5.

You can see more evidence of what window object actually is in window.constructor, which will show it as being constructed from

function Window() { [native code] }

As such, we have no real way of determining why certain properties are enumerable (according to Chrome), and others are not from within the JavaScript runtime.

However, you can still see the full state of Chrome's inconsistent window enumerable properties by looking at Object.keys(window) which according to its MDN article, "returns an array of all own enumerable properties found upon a given object."

Doing so still returns an array of 30-odd properties. Chalk it up to Chrome strangeness!

EDIT: With some further testing, I found the proper way for Chrome to make sense as to window's enumerable properties

Object.keys(window).filter(function(prop) {
  return window.hasOwnProperty(prop) && !window.propertyIsEnumerable(prop);
});

It would seem that in Chrome, the candidate list of properties for enumeration by for...in is NOT what is returned by Object.keys, but actually much closer to Object.getOwnPropertyNames which lists enumerable and non-enumerable properties. However, even that is inconsistent. The list of properties that are window.hasOwnProperty(prop) && !window.propertyIsEnumerable(prop) come out to 423 and 454 for for...in and Object.getOwnPropertyNames, respectively, in my testing.

Aintaer
  • 2,131
  • 17
  • 15
  • 1
    What's even weirder is that I'm only seeing 8 properties with `Object.keys`. Perhaps it should be seen as a bug :) – pimvdb Sep 15 '12 at 09:18
  • Even weirder is the inconsistent behavior of ``for...in`` on Chrome when it comes to enumerability of host properties. I found another test with inconsistent results. – Aintaer Sep 15 '12 at 17:23
  • Fiddling with it, I found that Chrome does in fact seem to honour `hasOwnProperty` when using `Object.keys`. That is, `return !window.propertyIsEnumerable(prop);` is enough. Still, this shouldn't be necessary of course. – pimvdb Sep 15 '12 at 17:48
0

Chrome is weird:

for(a in window)
    if(window.hasOwnProperty(a) && window.propertyIsEnumerable(a))
        console.log(a);

It puts out about 30 properties compared to like 450 the other way, in chrome.

PitaJ
  • 12,969
  • 6
  • 36
  • 55
  • You would not need `hasOwnProperty` at all - only own property will be tested as enumerable – Bergi Aug 27 '12 at 17:32