DontEnumDontEnum ]
]DontEnum attributeECMAScript 3 has an internal attribute called DontEnum. This attribute is
attached to certain properties by default
(§8.6.1).
The internal DontEnum attribute determines what is not to be enumerated by a
for-in enumeration (§12.6.4).
propertyIsEnumerable Test
The internal DontEnum attribute
can be read by using Object.prototype.propertyIsEnumerable( s ),
but cannot be set or modified. Due to a defect in the spec, and defects in the browsers,
this method doesn't tell you what will or will not show up in
a for in loop. I'll explain why later.
(function(){
var f = function Garrett(){}
f.constructor = "Monkey";
var isEnumerable = f.propertyIsEnumerable('prototype');
var isEnumerable2 = f.propertyIsEnumerable('constructor');
return isEnumerable + " " + isEnumerable2;
})();
Should be: true true
| Browser: | Internet Explorer | Mozilla | Opera | Safari 2 | Safari 3 |
|---|---|---|---|---|---|
| Result: | false false | true true | false true | error | true true |
The browsers clearly disagree. There are other browser bugs and problems with the ECMA-262 spec itself, as we will see.
Safari 2 does not support Object.prototype.propertyIsEnumerable. This is fixed in Safari 3.
The tests in this article use the following function in lieu of Safari's missing
Object.prototype.propertyIsEnumerable.
/** For safari 2's missing Object.prototype.propertyIsEnumerable */ function isPropertyEnumerable(o, p) { if( Object.prototype.propertyIsEnumerable ) { return Object.prototype.propertyIsEnumerable.call( o, p ); } if(o.hasOwnProperty(p)) { for( var prop in o ) { if(prop == p) return true; } } return false; }
The above function is inefficient and is in not presented as a solution for production code. It is simple, it works in Safari 2, and I need it for the examples.
I named it isPropertyEnumerable for two reasons:
propertyIsEnumerable. This point avoids a nasty bug in Opera, where
modifying window.propertyIsEnumerable would have
the effect of modifying Object.prototype.propertyIsEnumerable (link).
DontEnum BugDontEnum
JScript will skip over any property in any object where
there is a same-named property in the object's prototype chain that has the
DontEnum attribute. If a property with the DontEnum attribute exists in the prototype chain,
or if the
instance property is marked DontEnum, it is not enumerated, regardless of programmer
defined values for
that property. JScript does not properly check the DontEnum atribute.
This can be easily demonstrated by creating an object and then enumerating over its properties.
(function(){ var obj = { constructor : function() { return 0; } ,toString : function() { return "1"; } ,valueOf : function() { return 2; } ,toLocaleString : function() { return "3"; } ,prototype : function() { return "4"; } ,isPrototypeOf : function() { return 5; } ,propertyIsEnumerable : function() { return 6; } ,hasOwnProperty : function() { return 7; } ,length: function() { return 8; } ,unique : function() { return "9" } }; var result = []; for(var prop in obj) { result.push(obj[ prop ]()); } return result.join(""); })();
Should be:0123456789
| Browser: | Internet Explorer | Mozilla | Opera | Safari 2 | Safari 3 |
|---|---|---|---|---|---|
| Result: | 489 | 0123456789 | 0123456789 | 0123456789 | 0123456789 |
The JScript DontEnum bug effectively breaks Object as a hashtable. Quite a serious problem.
Nothing will force IE to properly recognize the DontEnum attribute.
It is interesting that IE enumerated the prototype property because
Object.prototype has the DontEnum attribute. IE will not enumerate a prototype
property for Function objects.
There are some examples later on that show how to borrow methods in Internet Explorer, working around this problem.
I first noticed this problem back in 2002. I had a simple test case that seemed to give the wrong result.
It was explained to me that
toString is non-enumerable, even if overridden. I accepted
this advice from Dr. Tom Trenka
and Michael van Ouwerkerk, the latter of whom
who correctly and concisely described typing in JavaScript:
"Variables are not typed in JavaScript, but their values are." (most people misunderstand this). Both intelligent, successful
programmers.
Later on, I aggressively pursued the correct answer. I felt that JScript was wrong. I read the ECMAScript-262 specification. It turns out, JScript is wrong, still to this day. §12.6.4 describes the algorithm for for-in enumeration:
§12.6.4 The
for-inStatement The productionIterationStatement : for ( VariableDeclarationNoIn in Expression ) Statementis evaluated as follows:
- Evaluate VariableDeclarationNoIn.
- Evaluate the Expression.
- Call
GetValue(Result(2)).- Call
ToObject(Result(3)).- Let V = empty.
- Get the name of the next property of
Result(4)that doesn't have theDontEnumattribute.- Evaluate
Result(1)as if it were an Identifier; (yes, it may be evaluated repeatedly).- Call
PutValue(Result(7), Result(6)).- ...
The problem is that JScript does not properly check the DontEnum attribute in step 6.
Get the name of (
the next property of Result(3) that doesn't have the
DontEnum attribute ).
Instead of checking the DontEnum attribute, JScript will skip over any property in any object where
there is a same-named property in the object's prototype chain that has the attribute
DontEnum.
To understand what this means, it is necessary to define what the prototype chain is.
Next in this tutorial: Prototype Chain