ECMAScript DontEnum attribute

  • Revision slug: ECMAScript_DontEnum_attribute
  • Revision title: ECMAScript DontEnum attribute
  • Revision id: 63934
  • Created:
  • Creator: Dhtmlkitchen@gmail.com
  • Is current revision? No
  • Comment /* JScript DontEnum Bug */

Revision Content

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

})();

eval

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 Error?

Safari 2 does not support Object.prototype.propertyIsEnumerable. This is fixed in Safari 3.


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.


JScript DontEnum Bug

JScript has a Long-standing Bug with its Implementation of DontEnum

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 attribute.

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("");
})(); 

eval

Should be:0123456789

Browser: Internet Explorer Mozilla Opera Safari 2 Safari 3
Result: 489 0123456789 0123456789 0123456789 0123456789

We've got a problem!

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. Discovery

JScript has never fixed this bug. §12.6.4 describes the algorithm for for-in enumeration:

   12.6.4 The for-in Statement

The production IterationStatement : for ( VariableDeclarationNoIn in Expression ) Statement is evaluated as follows:

      1. Evaluate VariableDeclarationNoIn.
      2. Evaluate the Expression.
      3. Call GetValue(Result(2)).
      4. Call ToObject(Result(3)).
      5. Let V = empty.
      6. Get the name of the next property of Result(4) that doesn't have the DontEnum attribute.
      7. Evaluate Result(1) as if it were an Identifier; (yes, it may be evaluated repeatedly).
      8. Call PutValue(Result(7), Result(6)).
      9. ...

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.

ach object has an associated Prototype object (§4.3.5, §8.6.2).

8.6.2 Internal Properties and Methods Native ECMAScript objects have an internal property called Prototype. The value of this property is either null or an object and is used for implementing inheritance. Properties of the Prototype object are visible as properties of the child object for the purposes of Get access, but not for Put access.

The prototype chain (§4.2.1) is a linkage of Prototype objects

4.3.5 Prototype A Prototype is an object used to implement structure, state, and behaviour inheritance in ECMAScript. When a constructor creates an object, that object implicitly references the constructor's associated prototype for the purpose of resolving property references. The constructor's associated prototype can be referenced by the program expression constructor.prototype, and properties added to an object's prototype are shared, through inheritance, by all objects sharing the prototype.

JavaScript uses the prototype chain, a linkage of the internal Prototype objects, with the internal Get(P) method (§8.6.2.1) to resolve property reference identifiers.

For Put access (or assignment), the prototype is not used.

function X(){

}

X.prototype.inited = false;

var y = new X;
y.inited = true;

var z = new X;
z.inited; 

The assignment y.inited = false does not change the value of X.prototype.inited, so z.inited is false.

Revision Source

<p>ECMAScript 3 has an internal attribute called DontEnum. This attribute is attached to certain properties by default (§8.6.1).
</p><p>The internal DontEnum attribute determines what is not to be enumerated by a for-in enumeration (§12.6.4).
propertyIsEnumerable Test
</p><p>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.
</p>
<pre>(function(){

var f = function Garrett(){}
f.constructor = "Monkey";
var isEnumerable = f.propertyIsEnumerable('prototype');
var isEnumerable2 = f.propertyIsEnumerable('constructor');
return isEnumerable + " " + isEnumerable2;

})();
</pre>
<p>eval
</p>
<table border="1" class="result">
		<tbody><tr>

			<th scope="row">Browser:</th>
			<th>Internet Explorer</th>
			<th>Mozilla</th>
			<th>Opera</th>
			<th>Safari 2</th>
			<th>Safari 3</th>
		</tr>
		<tr>
			<th scope="row">Result:</th>
			<td class="incorrect">false false</td>
			<td>true true</td>
			<td class="incorrect">false true</td>
			<td class="error">error</td>
			<td>true true</td>
		</tr>
</tbody></table>
<p>The browsers clearly disagree. There are other browser bugs and problems with the ECMA-262 spec itself, as we will see.
</p>
<h4 name="Safari_2_Error.3F"> Safari 2 Error? </h4>
<p>Safari 2 does not support Object.prototype.propertyIsEnumerable. This is fixed in Safari 3.
</p><p><br>
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.
</p><p><br>
</p>
<h3 name="JScript_DontEnum_Bug"> JScript DontEnum Bug </h3>
<p>JScript has a Long-standing Bug with its Implementation of DontEnum
</p><p>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 attribute.
</p><p>This can be easily demonstrated by creating an object and then enumerating over its properties.
</p>
<pre>(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("");
})(); 
</pre>
<p>eval
</p><p>Should be:0123456789
</p>
<table border="1" class="result">
		<tbody><tr>
			<th scope="row">Browser:</th>
			<th>Internet Explorer</th>
			<th>Mozilla</th>
			<th>Opera</th>
			<th>Safari 2</th>
			<th>Safari 3</th>
    	</tr>
		<tr>
			<th scope="row">Result:</th>
			<td class="incorrect">489</td>
			<td>0123456789</td>
			<td>0123456789</td>
			<td>0123456789</td>
			<td>0123456789</td>
		</tr>
</tbody></table>
<p>We've got a problem!
</p><p>The JScript DontEnum bug effectively breaks Object as a hashtable. Quite a serious problem. Nothing will force IE to properly recognize the DontEnum attribute.
</p><p>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.
</p><p>There are some examples later on that show how to borrow methods in Internet Explorer, working around this problem.
Discovery
</p><p>JScript has never fixed this bug. §12.6.4 describes the algorithm for for-in enumeration:
</p>
<pre class="eval">   12.6.4 The for-in Statement
</pre>
<p>The production IterationStatement : for ( VariableDeclarationNoIn in Expression ) Statement is evaluated as follows:
</p>
<pre class="eval">      1. Evaluate VariableDeclarationNoIn.
      2. Evaluate the Expression.
      3. Call GetValue(Result(2)).
      4. Call ToObject(Result(3)).
      5. Let V = empty.
      6. Get the name of the next property of Result(4) that doesn't have the DontEnum attribute.
      7. Evaluate Result(1) as if it were an Identifier; (yes, it may be evaluated repeatedly).
      8. Call PutValue(Result(7), Result(6)).
      9. ...
</pre>
<p>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 ).
</p><p>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.
</p><p>To understand what this means, it is necessary to define what the prototype chain is.
</p><p>ach object has an associated <a href="en/Prototype">Prototype</a> object (§4.3.5, §8.6.2).
</p>
<blockquote>
    8.6.2 Internal Properties and Methods

    Native ECMAScript objects have an internal property called <a href="en/Prototype">Prototype</a>. The value of this property is either null or an object and is used for implementing inheritance. Properties of the <a href="en/Prototype">Prototype</a> object are visible as properties of the child object for the purposes of <a href="en/Get">Get</a> access, but not for <a href="en/Put">Put</a> access.
</blockquote>
<p>The prototype chain (§4.2.1) is a linkage of <a href="en/Prototype">Prototype</a> objects
</p>
<blockquote>
    4.3.5 Prototype

    A <a href="en/Prototype">Prototype</a> is an object used to implement structure, state, and behaviour inheritance in ECMAScript. When a constructor creates an object, that object implicitly references the constructor's associated prototype for the purpose of resolving property references. The constructor's associated prototype can be referenced by the program expression constructor.prototype, and properties added to an object's prototype are shared, through inheritance, by all objects sharing the prototype.
</blockquote>
<p>JavaScript uses the prototype chain, a linkage of the internal <a href="en/Prototype">Prototype</a> objects, with the internal <a href="en/Get">Get</a>(P) method (§8.6.2.1) to resolve property reference identifiers.
</p><p>For <a href="en/Put">Put</a> access (or assignment), the prototype is not used.
</p>
<pre>function X(){

}

X.prototype.inited = false;

var y = new X;
y.inited = true;

var z = new X;
z.inited; 
</pre>
<p>
 The assignment <code>y.inited = false</code> does not change the value of <code>X.prototype.inited</code>, so <code>z.inited</code> is <code>false</code>. 
</p>
Revert to this revision