The for-in
loop is the only cross-browser technique for iterating the properties of generic objects. There’s a bunch of literature about the dangers of using for-in
to iterate arrays and when to apply the hasOwnProperty
filter, but beyond that, documentation of this ubiquitous construct is surprisingly patchy. This article attempts to fill some gaps, I hope its useful.
The Basics
The ES 5 specification details two distinct syntaxes for the for-in
statement:
1. for (var variable in objectExpression) {statement}
This is the familiar format. Any expression that evaluates to an object may be used as the objectExpression. If a primitive is supplied it will be coerced to an object. The properties of this object are iterated. On each iteration the name of the property is assigned to the declared variable and the statement (if present) is evaluated.
var myObj = {a: 1, b: 2, c: 3}, myKeys = []; for (var property in myObj) { myKeys.push(property); } myKeys; //['a','b','c'];
The variable can optionally be defined outside of the for-in
production. The curly brackets are only required if the statement spans multiple lines and the statement itself is optional. Therefore the following code is also valid – though not terribly useful unless you are interested in recording the name of myObj’s “last” property (more about iteration sequence later).
var myObj = {a: 1, b: 2, c: 3}, lastProperty; for (lastProperty in myObj); lastProperty; //"c";
Here’s another example. In this case the objectExpression resolves to a primitive:
var str = "hello!", spreadOut = ""; for (var index in str) { (index > 0) && (spreadOut += " ") spreadOut += str[index]; } spreadOut; //"h e l l o !"
Note that as with all property names, the indexes in the above example are actually strings – so we cannot do a simple “truthy” test on line 5. Later on we’ll see why Strings and Arrays are not always good candidates for for-in
iteration.
2. for (LeftHandSideExpression in objectExpression) {statement}
This interesting syntax is seldom documented (MDC gives it no mention). In ECMAScript terms a LeftHandSideExpression is any expression that resolves to a property reference (think anything that can go on the left hand side of an assignment). On each iteration, the name of the next property gets assigned to the evaluation of the LeftHandSideExpression. It’s perfectly valid for the LeftHandSideExpression to resolve to a different reference on each iteration. Occasionally this is useful – even elegant – for example getting an array of property names is now a breeze:
var myObj = {a: 1, b: 2, c: 3}, myKeys = [], i=0; for (myKeys[i++] in myObj); myKeys; //['a','b','c'];
Which properties are iterated?
This requires some knowledge of internal JavaScript properties. Objects are collections of properties and every property gets its own standard set of internal properties. (We can think of these as abstract properties – they are used by the JavaScript engine but they aren’t directly accessible to the user. ECMAScript uses the [[property]] format to denote internal properties).
One of these properties is [[Enumerable]]
. The for-in
statement will iterate over every property for which the value of [[Enumerable]]
is true. This includes enumerable properties inherited via the prototype chain. Properties with an [[Enumerable]]
value of false, as well as shadowed properties (i.e. properties which are overridden by same-name properties of descendant objects) will not be iterated.
In practice this means that, by default, for-in
loops will pick up every non-shadowed, user-defined property (including inherited properties) but not built-in properties. For example Object’s built-in functions (such as toString
) will not be enumerated.
This also means that if you are in the habit of augmenting the prototypes of built-in objects, then your custom extensions will also show up:
var arr = ['a','b','c'], indexes = []; Array.prototype.each = function() {/*blah*/}; for (var index in arr) { indexes.push(index); } indexes; //["0", "1", "2", "each"] whoops!
Some frameworks (e.g. Prototype.js and Mootools) add a lot of custom prototype augmentation and using for-in
to iterate Arrays and Strings is generally considered a bad idea. Using a regular for
loop is a good alternative for Array and String iteration. In addition, ES5 defines a bunch of custom Array iterators (forEach
, map
etc). Unfortunately non of these alternate iteration strategies work with regular Objects – which is why its considered very bad practice to augment Object.prototype
.
The “DontEnum” bug
IE<9 suffers from a serious iteration quirk whereby properties that shadow built-in (and therefore non-enumerable or [[DontEnum]] in ES3 parlance) properties will also not be enumerated.
var obj = {
a: 2,
//shadow a non-enumerable
toString: "I'm an obj"
},
result = [];
for (result[result.length] in obj);
result;
//IE<9 -> ["a"]
//Other browsers -> ["a", "toString"]
(Thanks to @kangax for the reminder and @skilldrick for the neat variation on for (result[i++] in obj);
Can I prevent certain properties from being iterated?
Yes. There are a couple of standard techniques for filtering out unwanted members from our for-in
loops:
1. Object.prototype.hasOwnProperty
This function will invoke the property’s internal [[GetOwnProperty]] method to determine whether the given property is defined directly on the object (instead of somewhere in the prototype chain).
var arr = ['a','b','c'], indexes = []; Array.prototype.each = function() {/*blah*/}; for (var index in arr) { if (arr.hasOwnProperty(index)) { indexes.push(index); } } indexes; //["0", "1", "2"]
JSLint expects you to always wrap the body of a for-in
with an if
statement even when iterating a regular object (never mind that you could just as easily assert the condition with an &&
instead of an if
!)
If you’re paranoid that you or someone else might override the local definition of hasOwnProperty
you can invoke the prototype reference directly
//snip... for (var index in arr) { if (Object.prototype.hasOwnProperty.call(arr, index)) { indexes.push(index); } }
2. Object.defineProperty
ES5 introduces a new method on Object which allows properties to be defined with custom internal property settings (not supported in FF<4 and IE<9)
var obj = {}; Object.defineProperty( obj, "value", { value: true, writable: false, enumerable: true, configurable: true });
We can leverage this to set our own value for [[Enumerable]] allowing us to hide custom prototype augmentations from the for-in
iterator
var arr = ['a','b','c'], indexes = []; Object.defineProperty(Array.prototype, "each", { value: function() {/*blah*/}, writable: false, enumerable: false, configurable: false }); for (var index in arr) { indexes.push(index); } indexes; //["0", "1", "2"]
What is the iteration sequence?
The ECMA standard does not specify an enumeration order but the de facto standard for non-array objects is to enumerate properties according to the order of their original assignment.
var obj = {a: 1, b: 2, c: 3}, result = []; obj.e; //referenced but not assigned obj.f = 'bar'; //1st assignment obj.e = 4; obj.dd = 5; obj.f = 'foo'; //2nd assignment for (var prop in obj) { result.push(prop); } result.toString(); //"a,b,c,f,e,dd"
However there are currently a couple of important exceptions you should be aware of:
Deleting properties in IE
In IE deleting a property and then redefining it does not update its position in the iteration sequence. This contrasts with the behaviour observed in all other major browsers:
var obj = {a: 1, b: 2, c: 3}, result = []; delete obj.b; obj.b = 4; for (var prop in obj) { result.push(prop); } result.toString(); //IE ->"a,b,c" //Other browsers -> "a,c,b"
Numerically named properties in Chrome
Chrome browsers process numerically named keys first and in numeric sequence not insertion sequence.
var obj = {3:'a', 2:'b', 'foo':'c', 1:'d'}, result = []; for (var prop in obj) { result.push(prop); } result.toString(); //Chrome -> "1,2,3,foo" //Other browsers -> "3,2,foo,1"
There’s a bug logged for it together with a gazillion comments forming a raging back and forth argument as to whether it should be fixed. To my mind this is a bug that needs fixing. Sure properties of regular objects are by definition unordered, and yes ECMA has not yet defined a standard – but as John Resig and Charles Kendrick point out, the lack of an ECMA standard is no excuse – standards generally follow implementation and not vice versa – and in this case chrome is out of line.
The in
operator
This nifty cousin of for-in
uses the internal [[HasProperty]] method to check for the existence of a named property in a given object:
propertyNameExpression in objectExpression
In pseudo code terms it works something like this:
var name = //resolve [propertyNameExpression]; var obj = //resolve [objectExpression]; return obj.[[HasProperty]](name);
Here’s some usage examples:
var obj = {a:1, b:2, c:undefined, d:4}, aa = {}; 'b' in obj; //true 'c' in obj; //true ('undefined' but still exists) 'e' in obj; //false (does not exist) delete obj.c; 'c' in obj; //false (no longer exists) obj.e; 'e' in obj; //false (referenced but not assigned) //resolving expressions aa.o = obj; aa.a = 'a'; aa.a in aa.o; //true
Notice how 'c' in obj
returns true even though the value of o.c
is undefined
. The internal method [[HasProperty]] will return true for any assigned property regardless of value. This is useful for distinguishing those properties that are deliberately assigned undefined
from those that simply do not exist.
Like the for-in
loop, the in
operator will search in the object’s prototype chain. Unlike the for-in
loop, the in
operator does not distinguish enumerable and non-enumerable properties:
var arr = [true,false,false]; 1 in arr; //true 'slice' in arr; //true 'toString' in arr; //true
And that’s about all. Feel free to comment with suggestions, omissions or complaints 😉
Further Reading
Resig, John: JavaScript in Chrome
V8 Bug Log: Wrong order in Object properties interation [sic]
ES 5 Discussion: Yet more ambiguities in property enumeration
ECMA-262 5th Edition:
8.6.1 Property Attributes (includes [[Enumerable]])
8.12.1 [[GetOwnProperty]]
8.12.6 [[HasProperty]]
11.2 Left-Hand-Side-Expressions
11.8.7 The in
Operator
12.6.4 The for-in
Statement
15.2.4.5 Object.prototype.hasOwnProperty
The first example says “myKeys.push(myObj)”, instead of “myKeys.push(myObj[property])”…
The first example is wrong.
It should be
myKeys.push(property);
instead of
myKeys.push(myObj);
You could simplify that one with myKeys to:
Nice little snippet there, thanks!
Yep – even better! I added a new section on the “DontEnum” bug with an example formatted in the manner of your suggestion – thanks!
Actually, that one has a bit of a performance penalty. You’re evaluating .length with every iteration. Not a problem for small objects though.
In the first code listing, you are pushing the object being iterated-upon onto the
myKeys
array, where I think you mean to push the properties of that object as it’s being iterated.I think line 4 in your first code sample should be:
myKeys.push(property);
In the first example; by
myKeys.push(myObj);
you mean
myKeys.push(property);
😀
@anon @Andreas @Erik @indy @rasmusfl0e
Thanks for paying attention guys – 😉
Did you forgot ‘for each .. in” on purpose ?
see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for_each…in
Yes 😉 Because it’s Mozilla only- same with Iterator – focusing on ES5
A better alternative for your third example in section `1.` would be
var str = ‘hello!’, spreadOut;
spreadOut = str.split(”).join(‘ ‘);
document.write(spreadOut); //outputs “h e l l o!”
Yep that works well – I just wanted to provide an example of how for-in works with primitives
Great article Angus!
If you want to include ES5, don’t forget Object.keys and Object.getOwnPropertyNames.
Never knew that any left hand side expression could be in a for-in loop, but I guess it makes sense. Now to see what cool, concise things I can do with it…
Thanks Nick
>>Never knew that any left hand side expression could be in a for-in loop, but I guess it makes sense. Now to see what cool, concise things I can do with it…
Sadly if they had allowed commas in leftHandSideExpression it might have been cooler, more concise, as in:
for ((while (arr[i++]), arr[i]) in obj)
but @kangax tweeted a cool one:
Object.defineProperty(this, ‘log’, {set: console.log.bind(console)});
for (log in window);
Notice also, that “side-effect” properties, e.g. added/deleted during the enumeration are also not specified to be enumerated (try in different implementations, I remember some version of IE have/had a it’s own behavior).
The current reason of not specifying the exact order is of course in optimization. Dense arrays are stored as real C-arrays in JS. And when some non-integer property is added, they are rebuilt into a (less-, non-optimized) hash-table.
For example in Firefox:
will alert 0, 1, 2.
Now if you we have
we get 0, 1, 2, “x”
However, if we start out with x, then we have:
Will alert x, 2, 1, 0
Andreas Gal figured this out in es-discuss thread: https://mail.mozilla.org/pipermail/es-discuss/2010-December/012464.html
Dmitry.
Thanks Dmitry,
You bring up an important issue with sequence of side-effect properties. I would like to investigate further.
As for array enumeration sequence – yes it’s a rats nest – notice in my article I deliberately avoided going there by specifically talking about ‘non-array’ objects in the sequence section. 😉
I think it’s ok to not worry too much about array iteration sequence for the case of arrays with non numeric keys since it is well established that using for-in over such arrays is bad practice. Indeed in the chrome sequence bug I notice the argument is made that safari and ff disagree over sequence but they use an array with non numeric keys as an example – which seems to miss the point (deliberately ;-)?). There are plenty of good iteratation techniques for arrays – so the sequence of for-in iteration over arrays should not be an issue for developers.
Having said all that it is sometimes interesting to explore such browser differences because, as you suggest, it offers insight into the workings of the respective JS engines.
Angus
is one of the most brilliant lines of JavaScript I have seen in quite some time.
Thanks Darren!
IE9 does not enumerate through properties the same as IE8 and other browsers either. You may want to update this article with this info: http://msdn.microsoft.com/en-us/library/gg622937.aspx
Really the best Article I have seen about “for in and object.prototype”.
Great Job
Re: Loops in javascript
Hello Everyone,
please visit the following links for more details on loops in javascript……………..
http://mindstick.com/Articles/0afc1f6e-e244-4859-8d02-d6cb6f1cf745/?Loops%20in%20Java%20Script
thanks !!!!!!
There is a fine alternative too the standard javascript loops: http://www.turboid.net/artikel/real-loops-in-javascript.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
gistfile1.js
hosted with ❤ by GitHub
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
gistfile1.js
hosted with ❤ by GitHub
Hi,
Here You can Find More information about for loop and how it is Performing,
http://antguider.blogspot.in/2012/06/javascript-tips-and-tricks-part-i.html
http://antguider.blogspot.in/2012/06/java-tips-and-tricks-part-i.html
An important quirk is missing, I think: if you “override” an enumerable property with a non-enumerable (ES5), it will still be enumerated in a for..in loop in IE9 and current Chrome version (see: http://stackoverflow.com/questions/13714938/inherited-non-enumerable-properties-in-for-in-loop-javascript)
The line `for (var LeftHandSideExpression in objectExpression)` is incorrect, the `var` keyword is a syntax error.