for-in evaluation order
Brendan Eich
brendan at mozilla.com
Mon Dec 27 13:36:14 PST 2010
The es-discuss and es5-discuss lists have seen this topic before. I don't always remember names of people who sent messages, but I keep complete archives, search them locally, then search the web for a decent archive. Possibly others do the same and can provide links.
Here's a good one to read, from Kent Hansen of Nokia in 2009:
https://mail.mozilla.org/pipermail/es5-discuss/2010-February/003484.html
Included below (inline to avoid deletion from the web archive) is Kent's testcase.
Interoperation requires "own" or direct property enumeration order to be insertion order for non-Array objects, so browsers do that.
Differences for prototype properties were the topic of Kent's post.
Arrays that are sufficiently dense, or all Arrays, or even all indexed properties depending on implementation, may be enumerated in index numerical order.
With ES5, for-in enumeration order (whatever it is, according to the implementation) comes up in other places: 15.2.3.7 Object.defineProperties and 15.2.3.14 Object.keys.
I don't think ECMA-262 should continue to underspecify enumeration order. No competitive browser can afford to randomize just to wean the Web off of the de-facto standard partial order. So it seems to me we should try to write a normative spec everyone likes that is sufficiently web-compatible and sensible. Dave took a swing at this:
http://wiki.ecmascript.org/doku.php?id=strawman:enumeration
/be
----- HTML-based test from kent.hansen at nokia.com -----
<html>
<head>
<script>
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
function write(str) { document.write(str); }
(function() {
write("Enumeration order: ");
var prot = {b:2};
var o = Object.create(prot);
o.a = 1;
var props = [];
for (var p in o)
props.push(p);
write(props.join(" "));
write("<br>");
})();
(function() {
write("Case I: ");
var prot = {};
var o = Object.create(prot);
o.a = 1;
var props = [];
for (var p in o) {
if (p == "a")
prot.b = 2;
props.push(p);
}
write(props.join(" "));
write("<br>");
})();
(function() {
write("Case II: ");
var prot = {};
var o = Object.create(prot);
o.a = 1;
var props = [];
for (var p in o) {
if (p == "a") {
delete o.a;
prot.a = 2;
}
props.push(p);
}
write(props.join(" "));
write("<br>");
})();
(function() {
write("Case III: ");
var prot = {b:2};
var o = Object.create(prot);
o.a = 1;
var props = [];
for (var p in o) {
if (p == "a")
o.b = 3;
props.push(p);
}
write(props.join(" "));
write("<br>");
})();
(function() {
write("Case IV: ");
if (this.__proto__ == undefined) {
write("not testable (requires __proto__ extension)<br>");
return;
}
var prot = {b:2};
var o = Object.create(prot);
o.a = 1;
var props = [];
for (var p in o) {
if (p == "a")
o.__proto__ = {c:3};
props.push(p);
}
write(props.join(" "));
write("<br>");
})();
</script>
</head>
<body>
</body>
</html>
More information about the es-discuss
mailing list