You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

273 lines
6.1 KiB

=========================
Object enumeration issues
=========================
.. note:: This document is quite incomplete. Tests should be expanded
so that the same enumeration test is executed at least in
Rhino and V8.
Key order during enumeration
============================
Ecmascript E3 or E5 do not require a specific key order during enumeration.
However, some existing code apparently relies on some ordering behavior:
* http://code.google.com/p/chromium/issues/detail?id=2605
* http://ejohn.org/blog/javascript-in-chrome/
However, specification is quite different from implementation. All modern
implementations of ECMAScript iterate through object properties in the
order in which they were defined. Because of this the Chrome team has
deemed this to be a bug and will be fixing it.
Apparently the behavior relied on is roughly:
* For arrays, return all used array index keys in ascending order first
* Then return all other keys in the order in which they have been first
defined
* The properties of the object itself are enumerated first, followed by
its prototype's properties, and so on
Specification (E5)
==================
A specific ordering is not required:
* Section 12.6.4 (The for-in statement):
The mechanics and order of enumerating the properties [...] is not
specified.
However, if an implementation provides a consistent ordering, it must do
so in all relevant situations:
* Section 15.2.3.7 (Object.defineProperties):
If an implementation defines a specific order of enumeration for the
for-in statement, that same enumeration order must be used to order
the list elements in step 3 of this algorithm.
* Section 15.2.3.14 (Object.keys):
If an implementation defines a specific order of enumeration for the
for-in statement, that same enumeration order must be used in step 5
of this algorithm.
As a side note, E5 defines a specific meaning for a "sparse" array in
Section 15.4: an array is sparse essentially if it contains one or more
"undefined" values in the range [0,length-1]. The "sparse" term used
occasionally in the Duktape implementation is unfortunately slightly
different (sparse enough to cause array part to be abandoned).
Rhino 1.7 release 2 2010 02 06
==============================
For objects (no prototype)
--------------------------
::
js> var x = {};
x.foo = 1;
x.bar = 1;
x[0] = 1;
x.quux = 1;
x[3] = 1;
x[1] = 1;
x.foo = 2;
for (var i in x) { print(i); }
foo
bar
0
quux
3
1
The behavior is consistent: all keys (including array indices) are returned
in the order in which they are first defined. If a key is deleted and
re-added, its enumeration order changes::
js> var x = {};
x.foo = 1;
x.bar = 1;
for (var i in x) { print(i); };
foo
bar
js> delete x.foo;
x.foo = 1;
for (var i in x) { print(i); };
bar
foo
For arrays (no prototype)
-------------------------
::
js> var x = [];
x.foo = 1;
x[0] = 1;
x[3] = 1;
x[1] = 1;
x.bar = 1;
for (var i in x) { print(i); };
0
1
3
foo
bar
For small, dense arrays, the behavior is consistent: array keys (with
defined values) are enumerated first, followed by keys in definition order.
However, this behavior breaks down with sparse arrays::
// still OK
js> var x = [];
x.foo = 1;
x[0] = 1;
x[8] = 1;
x[5] = 1;
x.bar = 1;
for (var i in x) { print(i); };
0
5
8
foo
bar
// 1000 appears after keys
js> x[1000] = 1;
for (var i in x) { print(i); };
0
5
8
foo
bar
1000
// ... and is also followed by a newly defined key
js> x.quux = 1;
for (var i in x) { print(i); };
0
5
8
foo
bar
1000
quux
// here '9' is higher than last well-behaving index (8) but still
// enumerates before string keys -- while '10' enumerates like
// a string key
js> x[10] = 1; x[9] = 1; for (var i in x) { print(i); };
0
5
8
9
foo
bar
1000
quux
10
Objects (with prototype)
------------------------
One prototype level::
js> function F() { }
F.prototype = { foo: 1, bar: 1 };
x = new F();
x.abc = 1;
x.quux = 1;
for (var i in x) { print(i); }
abc
quux
foo
bar
Object's keys are enumerated first, then prototype's keys. Prototype
keys with same name as properties of the object are not enumerated::
js> function F() { }
F.prototype = { foo: 1, bar: 1 };
x = new F();
x.quux = 1;
x.foo = 1;
x.xyz = 1;
for (var i in x) { print(i); }
quux
foo
xyz
bar
Here ``foo`` is not enumerated again because it was already enumerated
as part of the object's keys.
Object with an Array prototype
------------------------------
::
// test 1
js> function F() { }
F.prototype = [1,2,3];
x = new F();
print("length: " + x.length);
for (var i in x) { print(i); }
length: 3
0
1
2
// test 2
js> x[1] = 9;
print("length: " + x.length);
for (var i in x) { print(i); }
length: 3
1
0
2
// test 3
js> x.length = 2; // sets enumerable own property 'length'
print("length: " + x.length);
for (var i in x) { print(i); }
length: 2
1
length
0
2
// test 4
js> x[10] = 10;
print("length: " + x.length);
for (var i in x) { print(i); }
length: 2
1
length
10
0
2
Test 1 demonstrates enumeration of an empty object whose prototype is
an array of three elements. Enumeration lists the prototype keys
("0", "1", "2").
Test 2 shows that object enumeration comes first ("1") followed by
prototype keys not "shadowed" by object keys ("0", "2"; "1" is shadowed).
Test 3 shows that even though the object itself is forced to be of
length 2, prototype enumeration still lists all keys of the prototype,
including "2" which is beyond the array length.
Test 4 shows that 'length' is not exotic for an object which has an
array as a prototype. Exotic semantics of 'length' do not apply to
the object because the property write goes to the object, which is not
an array. This also explains the result of test 3.