Enumerating JavaScript Objects
Enumeration lies at the heart of DOM Scripting:
var lists = document.getElementsByTagName("UL"); for (var i = 0; i < lists.length; i++) { lists[i].className = "menu"; } for (var i = 0; i < array.length; i++) { print(array[i]); } for (var key in object) { print(object[key]); }
Us JavaScripters are forever writing loops like these. To ease the strain on our keyboards
Mozilla recently introduced a handy forEach
method for arrays:
array.forEach(print);
If you don’t understand the code above then go and read the documentation.
That’s fine for arrays but what about DOM node lists? That’s where most of our loop writing is concentrated. Fortunately, the clever Mozilla developers provide generic array methods to help us:
var lists = document.getElementsByTagName("UL"); Array.forEach(lists, function(list) { list.className = "menu"; });
Cool huh? The Array.forEach
method treats any object passed to it as if it were an array. So long as that object has a length
property then we are OK.
We can easily implement this method for non-Mozilla browsers:
// array-like enumeration if (!Array.forEach) { // mozilla already supports this Array.forEach = function(object, block, context) { for (var i = 0; i < object.length; i++) { block.call(context, object[i], i, object); } }; }
I’ve been using this technique for enumeration quite a lot recently and I decided to extend the idea:
// generic enumeration Function.prototype.forEach = function(object, block, context) { for (var key in object) { if (typeof this.prototype[key] == "undefined") { block.call(context, object[key], key, object); } } }; // globally resolve forEach enumeration var forEach = function(object, block, context) { if (object) { var resolve = Object; // default if (object instanceof Function) { // functions have a "length" property resolve = Function; } else if (object.forEach instanceof Function) { // the object implements a custom forEach method so use that object.forEach(block, context); return; } else if (typeof object.length == "number") { // the object is array-like resolve = Array; } resolve.forEach(object, block, context); } };
This allows me to write loops without knowing what kind of object I’m dealing with:
function printAll() { forEach (arguments, function(object) { forEach (object, print); }); }; // or forEach (document.links, function(link) { link.className = "super-link"; }); // or forEach ([1, 2, 3], print); forEach ({a: 1, b: 2, c: 3}}, print); // etc
Explanation
The global forEach
function allows us to enumerate any object according to its type.
If the object is array-like (has a length property) then we enumerate it like an array.
All other objects are enumerated using the standard for var x in y
mechanism.
When enumerating over objects, the discovered keys are compared against Object.prototype
.
If the key is defined on the Object
object then it is not enumerated.
That means that you cannot enumerate the built-in methods like toString
and valueOf
.
The global forEach
function will delegate the enumeration of functions to Function.forEach
.
So, if you choose to enumerate over a Function
object you will skip the built-in methods there too.
The Kick-Ass Bit
Although I’ve defined a forEach
method on Function.prototype
this is never called
by the global forEach
function (except when you are enumerating functions).
I’ve provided this as a bonus feature.
Basically, by calling the
forEach
method on a function you can enumerate
an object and compare the keys with that function’s prototype.
That means that you will only enumerate custom properties of the object.
An example is required:
// create a class function Person(name, age) { this.name = name || ""; this.age = age || 0; }; Person.prototype = new Person; // instantiate the class var fred = new Person("Fred", 38); // add some custom properties fred.language = "English"; fred.wife = "Wilma";
Enumerate using the standard forEach
method:
forEach (fred, print); // => name: Fred // => age: 38 // => language: English // => wife: Wilma
Enumerate using the Person.forEach
method:
Person.forEach (fred, print); // => language: English // => wife: Wilma
Note that the properties defined on the prototype are not enumerated in the second example.
Conclusion
I’m totally in love with this technique. I now use it all the time. It makes my code more readable and I feel safer enumerating over objects on different platforms.
It also saves on typing.
Download: forEach.js
Comments (84)
Leave a comment
Comment: #1
I love this. Takes care of one of the biggest annoyances of dealing with objects in JS.
Comment: #2
2 things:
typeof this.prototype[key] == "undefined"
can be written asthis.hasOwnProperty(key)
Comment: #3
Very handy with jQuery
@sjoerd: strings are array-like Objects and contain characters. If you whant to iterate over the words or lines, use split():
forEach("string with words".split(' '), function() {...});
Comment: #4
In the name of all javascript-code-maintainers in the world who have had to figure out what other people’s “clever” code did, I would like to offer you a swift kick in the privates.
Thank you.
Comment: #5
Thank you.
Comment: #6
@Sjoerd –
forEach
) on the left. When enumerating arrays withmap
etc the iteration statments end up on the right and the code becomes difficult to read. The temptation is to chain your iteration methods and you end up with unreadable code.hasOwnProperty
is not implemented in IE5.0. It would be a shame to exclude that browser for the sake of one line of code.Good point about the
String
object. Maybe I’ll implement Christof’ssplit
idea to support iteration of character data.Comment: #7
@Christof – I just added this:
Comment: #8
typeof this.prototype[key] == "undefined" || this.prototype[key] !== object[key]
is a better approximation ofhasOwnProperty
. Although there is still the obvious edge-case and maybe you prefer the current behaviour.Comment: #9
Comment: #10
How about making the context default to the object? that could save some keystrokes.
block.call(context || object, object[key], key, object);
Comment: #11
@Brito – I’ve written support for
every
,map
, etc but it’s in a separate library which I haven’t yet released. I published this code because it was small and self-contained. And cool.I like your idea of defaulting to the current object. Let me ponder that…
Comment: #12
@Dean I don’t think defaulting “this” to the current object is such a good idea.
It differs from Firefoxes native behaviour, and the object is also available anyway, as
arguments[2]
within theblock
function.Comment: #13
@Már – you are probably right. I seem to remember going through that thought cycle a while back and gave up on the idea.
Comment: #14
Isn’t it true that
is faster than
because in the former, the “object.length” attribute is evaluated for each iteration, and counted every time it is accessed? (That might just be for arrays, or HTMLCollections, or something. But I’m sure I’ve heard this.)
Comment: #15
Awesome, Dean.
You could take this a step further and try to back-hack JavaScript 1.7’s iterators, the ultimate in polymorphism. You’d have a wrapper object that’d respond to
foo.next()
calls, then throw an exception when it’s spent, etc.Probably overkill, I admit. But I’ve been playing with the new JS 1.7 stuff and it’s making me salivate.
Comment: #16
@Andrew –
foo.next()
requires that you throw aStopIteraton
error in order to terminate the loop. I implemented something similar and ended up hating it. It is really hard to debug JavaScript when you are throwing errors in nested closures that you are passing around. I still have onethrow StopIteration
statement left in my library, it is nicely contained but I still want to get rid of it.Comment: #17
Andrea Giammarchi also wrote JSL Javascript Standard Library
that have lots of useful prototypes to support the new methods in ECMA that IE(but not only) doesn’t implement
take a look.
P.s. Dean, grrrreat Work!
Comment: #18
I lost a piece of comment , I was mean: “Andrea Giammarchi also wrote Array.forEach in JSL: Javascript Standard Library”, sorry for the mistake.
@Anthony Lieuallen : you are right, but the faster method is to write a while with a decrement variable because in javascript –i is a lot faster of i++ and a while is a lot faster that a for loop.
Comment: #19
@Anthony, you can’t do that as it’ll stop early if an array element is false or null.
@Kentaromiura, the speed up would be negligible compared to the time required for fetching the array element and calling the callback. And you’d have to either fetch the elements backwards (which is probably wrong and can be a lot slower, DOM methods are often optimized for forward iteration) or perform some arithmetic to get the correct element (which would take longer than the time saved). Premature optimization etc.
But, it’s actually better to write:
for (var i = 0, length = object.length; i < length; ++i)
. Not because it’s faster but because it’s how forEach is defined (although, it’s probably not a great idea to rely on such subtlties).Comment: #20
#19: ok, I wasn’t refer to this case when I say the –i, but a while is still faster than a for.
for (var i = 0, length = object.length; i < length; ++i)
is a good optimization, but no so good as
var i=0,length=object.length; while(i<length){ .. i++; }
Optimizations are good things once you have completed something. If you argue that a while is more verbose that a for, when you use a good compressor(packer) your code is not verbose anymore..
Comment: #21
There’s a great example of this in the Optimizing JavaScript for Execution Speed chapter at websiteoptimisation.com
Here’s the Flipped Loop with Optimized Reverse Count
Comment: #22
@Anthony Lieuallen:
doesn’t work in this case:
If you care about the speed try
In case you want really fast loops, unroll them.
But I guess that the function-call is a lot slower than everything that is done for managing the loops. I wouldn’t expect too much performance gain here.
Comment: #23
@Everyone – looping backwards is not an option.
Comment: #24
It’s all my fault, I’m a sort of Optimization Maniac because in my 486 every single optimization is a big improvement
so I’ve suggested the while solution instead of for
sorry for beginning this disgression ;p
Comment: #25
It got hidden in all the talk about optimization, but I wasn’t suggesting storing the length as an optimization but because it’s the right thing to do. From the mozilla documentation:
Comment: #26
@Daniel – ah OK. I thought I’d tested that. My mistake. I’ll update the code…
Comment: #27
doesn’t work in this case:
object = [42,16,0,false,true,’Hello World!’]; for (var i=0, obj=null; obj=object[i]; i++) { block.call(context, obj, i, object); } Why? I don’t understand
Comment: #28
@Alex – the “
obj=object[i]
” part offor (var i=0, obj=null; obj=object[i]; i++)
is evaluated as a conditional statement, and the for-loop is broken as soon as it returnsfalse
.Incidentally both
object[2]
(0) andobject[3]
(false) are bound to make thefor
-condition fail and thus leave the rest of the array items unprocessed.Comment: #29
Hello, could anyone say is it any usefull for those who use prototype.js with it’s each etc.? Thanks
Comment: #30
Dean,
Do you know of anything about the speed implications of instanceof in javascript? I use javascript quite a bit but have never looked into instanceof. I know in school we were always told to never use instanceof. We should always enumerate _whatever_ because instanceof is so slow. In this case however you are using instanceof for enumeration, (and what you have done here is really sweet) so I would like to know just how bad instanceof is.
Thanks, Wesley Walser
Comment: #31
@Wesley – in pure OO terms you shouldn’t use
instanceof
as type-checking is frowned upon. As far as JavaScript goes I am not aware of any speed issues. In this case, it only called once to determine whichforEach
method to use. It is not called inside any loops.Comment: #32
damn dean, good sh*t, when are you coming out with a book?
Comment: #33
[…] Previous post: ← Enumerating JavaScript Objects […]
Comment: #34
Who reads books when you’ve got Google?
Comment: #35
[…] Enumerating JavaScript Objects
LINK:http://dean.edwards.name/weblog/2006/07/enum/ […]
Comment: #36
@Dean – I’ve been thinking about this and the the bit where you extend
Function.prototype
bugs me a little bit. It seems kind of out of place next toArray.forEach
andString.forEach
.Could you explain to me why you chose against simply calling it
Object.forEach
.It seems to me that it would allow you to simplify the generic (global)
forEach
function quite a bit.Is the ability to call
objectInstance.forEach
the main reason for your decision?Comment: #37
@Dean – ok, sorry, I get it now why you go for the
Function.prototype.forEach
(my bad).However, the thing that still confuses me is that a basic
Object.forEach
seems missing from the example. Is that intentional?Comment: #38
I did some testing this morning, and finally realized the full implications of extending
Function.prototype
.Fucking. Awesome.
Comment: #39
@Már – Extending
Function.prototype
means that I can identify custom properties of an object. It also means that you can extend native objects and have safe enumeration. Yep, fucking. Awesome.Comment: #40
As far as execution speed is concerned, I’ve found that Array.forEach is *much* slower than equivalent non-1.5 code. Same with all other Array shorthands introduced in 1.5.
Comment: #41
typeof this.prototype[key] == “undefined”
I just wondering why this idiom is used all around over shorter and more explicit form: !(key in this.prototype)
VS
Comment: #42
@Nikola – Really? That is surprising. I just assumed that built-in methods were faster.
@Valery – we check for
undefined
because values offalse
or zero would both fail your test.Comment: #43
Dean,
Are you sure? You are checking for existence of key, hence operator “in” is better, IMO.
Do not confuse:
!(key in this.prototype)
and
!(this.prototype[key])
(I used the first form, you comment the second one
VS
Comment: #44
@Valery – My mistake. I misread your comment.
Yes, you could use that notation too but there are a couple of older platforms where it doesn’t work so
typeof
is safer.Comment: #45
@Dean – sadly, yes. I’ve been toying around with some large array operations recently (.forEach, .some, .every on 10.000+ elements) and tried the new Array functions inside an 1.5 extension, speed decrease by more than 50%. Even on the simplest iterations. Either it’s a bug, or the builtins do some implicit type evaluations that end up being quite expensive.
By the way, I’d be glad if you could cover the new Destructuring Assignment in JS 1.7 after the FF 2.0 release. I’ve found that even many Python programmers don’t know how to use it properly, or even know that it exists.
Comment: #46
@Nikola Klaric: I guess you don’t do an extra function call inside the loop in your non JS 1.5 code. To call the callback function the JS-interpreter has to generate a new context for local variables, execute that function and then find out if the context can be freed or if it contains values that are still referenced. That is rather expensive compared to just entering the block of a for-loop.
Comment: #47
@Christof: +1 Seems to be an issue: cost of executing “for” block N times is much lower then cost of N function invocations.
@Nikola: Just wondering how much it takes to process 10.000+ elements. I have (not the most complex) use case where it takes 3 second for IE to process less then 1000 elements (grouping of elements by attribute, original source is array, target is object with array properties).
VS
Comment: #48
Yep! That’s a cool way to iterate!
(And extending Function.prototype is useful for a lot of things :-D.)
@Dean: I almost never read books about programming any more, though I’ve seen that The Man in Blue and {ppk} released one each recently, so I might order those two. (If you wrote a book I might be tempted to read that one too.)
Comment: #49
What about: for (var i in x) (function() { … }).apply(x[i]);
Comment: #50
What about it?
Comment: #51
@dean, it might be worth noting excplicitly in the source code that the order is important. First you must check for the existence of and define
Array.forEach
and only then defineFunction.prototype.forEach
.I had already got
Array.forEach
in my toolkit, and after reading this blog-entry decided to add yourFunction.prototype.forEach
to the mix. I inadvertedly placed it above my oldArray.forEach
and then things started to behave strangely in Internet Explorer.It took me a while to figure out where I’d gone wrong.
Comment: #52
@Már – If you are smart enough to implement your own
Array.forEach
then you are smart enough to work out the rest. Just like you did.Comment: #53
Any particular reason why Function.prototype is extended with forEach but String.prototype isn’t?
Comment: #54
@MrBester – I think there is a good reason — I just can’t remember what I was thinking at the time.
For completeness it should probably be implemented on both interfaces.
Comment: #55
[…] Só complementando o post achei um exemplo bacana de prototipação de um forEach no Javascript. O mozilla já incorporou um objeto Array.forEach mas enquanto isso não abrange todos tá aí a função prontinha AQUI […]
Comment: #56
Regarding the Array forEach implementation for non-Mozilla browsers:
One also needs to provide an
Array.prototype.forEach
implementation like the following for a Mozilla compatible behaviour:Otherwise code like
[1, 2, 3].forEach(print)
will lead to an “Object doesn’t support this property or method” (or so) error.(If only the global forEach implementation is used all the time the Array.forEach function is sufficient.)
Comment: #57
Very nice, especially the check for the over-riding custom for each. Does this code make prototyping array, even object are of little concern?
Regarding speed here is a page to test the speed of functions I just put up: jsSpeedTester. Enjoy and thanks again for some head scatching Dean.
Comment: #58
@richard – Yes. Using this technique you can extend native JavaScript objects. See my subsequent post.
Comment: #59
Thanks Dean. I do believe that this code can find a good home in my library
Can’t believe that I have not stumbled across your site before now.
Comment: #60
Dean amazing piece of work I added a small number enumeration to the code a little while back and I modified the character enumeration. But I got a small question.
When doing the following
This is pretty straight forward but I wanted to have the “Kick Ass” touch you described at the bottom of original post. Then I did this
This will let you do things like ‘hello’.forEach(print), but for the number you will have to do var ten = 10; ten.forEach(print) as well as forEach(10, print) and forEach(‘hello’, print). My initial idea was to produce something like 10.forEach(print) but couldn’t do it what I got get close.
Does this make sense to you guy’s or am I doing something competely wrong? Is it ok to do block() instead of block.call() inside of the String and Number method?
the modified forEach.js file for easier reading
Comment: #61
@Laurent – To be compatible with other
forEach
functions you should also take acontext
argument. If you want to callforEach
on a number simply wrap the number in parentheses:I like the idea of
forEach
on numbers, it is a good way to loop a certain number of times although the passed arguments are of little value.Comment: #62
[…] Dean Edwards recently described a pretty cool technique for enumerating javascript objects, but (currently) it’s not appropriate for sparse arrays. In case you’re unfamiliar with sparse arrays (or I’m using the wrong term), in the language of the Mozilla documentation for Array.forEach, a sparse array is one that’s not “dense”. For example: […]
Comment: #63
[…] Javascript’s new forEach() function. Thanks to Dean Edwards, there is a cross browser solution, that is extremely simple to use. There are some other new functions as well that I haven’t explored yet, but they are worth noting. […]
Comment: #64
[…] The base2 core library is also quite useful in its own way. It includes things like my Base class and enumeration methods. I’ll document this library sometime in the future (it’s only little). For the time being I am only supporting the base2.DOM module and even then only if you are using the bind() method that I’ve demonstrated here. […]
Comment: #65
That is really awesome. I needed something like forEach a thousand times working with JS. PHP has a similar way of handling arrays too and it is much more simple. Nice work!
Comment: #66
Dean is there anyway to break out of the forEach? I wanted to use it for searching for a value in an array of objects but could not find a way to get it to “break” out of the loop. Had to revert to a for loop instead in the mean time. Not had enough tea to ponder it over enough properly yet.
Comment: #67
@Pete – You can
throw
to break out of a loop. Like this:Comment: #68
[…] Dean Edwards: Enumerating JavaScript Objects nifty js object iteration (tags: code tutorial usability javascript howto programming prototype reference ajax) […]
Comment: #69
I found this bit of code today, and well, pretty nice stuff. I was looking for a way to enumerate all objects currently created during a session using this forEach but have yet to succeed.
Let me elaborate.
I have a page that loads a bunch of javascript code, basically a jsp that has a bunch of script declarations.
type=”text/javascript” src=”my_scripts/audioPlayer.js”
type=”text/javascript” src=”my_scripts/videoPlayer.js”
When i use Firebug and look at the scripts i will find stuff like
audioPlayer Object
videoPlayer Object … …
Is it possible to enumerate all those Objects using forEach?
Any suggestion would be appreciated Thanks
Comment: #70
@Eric – you can use the
forEach
to enumerate anything.Comment: #71
Dean,
Thanks for your reply. I am able to enumerate most everything, yes, except what I have mentioned above. Chances are that your code works and the problem lies between the chair and the keyboard
Can you share some clues on how I should proceed?
Comment: #72
@Eric –
Not all objects have properties that can be enumerated however.
Comment: #73
Thanks for the reply Dean. Enumerating audioPlayer works, as expected, but the problem I have is to be able to get to audioPlayer.
I have a jsp page that includes js code and on loading said page, those script s will get executed and create objects (like audioPlayer and many more) and I am looking for a way of discovering them, i.e. enumerating them
Comment: #74
This is nice, and Mozilla is always ahead. However, I have to write cross browser code. So the awful question is ‘Does that awful browser, IE, also support foreach?’
Comment: #75
Comment 6 indicates that this code is intended to operate on IE 5.0, but it’s based on Function.call(), which according to MSDN was first implemented in IE 5.5. Is there some alternative?
Comment: #76
Can you make a good exampe on how to use this? I can’t make it work in my project….sorry i’m just a newbie…thanks
Comment: #77
Excellent article. Thanks.
It might be helpful to show how to create a simple print function like this for newbies:
Add this into your HTML document:
Comment: #78
Thank you for this. I think the best part is trying to understand how the code works: Javascript types are strange and this sheds some helpful light into some dark and scary places!
Comment: #79
@61 You should also be able to do this
10..forEach(function($){print($);});
Comment: #80
[…] this sent me on the hunt to see if it was at all possible. A furious Google search led me to a comment by Dean Edwards (who else?). He suggested throwing yourself out of the loop and wrapping the throw in a try catch […]
Comment: #81
[…] Re: What do you use instead of enum? On Oct 14, 5:15*am, Kenny <kentil…@gmail.com> wrote: > I wanted to do a state machine with symbolic states, but I see enum is > not part of the standard. I only have seven, do I just make seven vars > and hand enumerate? > > kt http://dean.edwards.name/weblog/2006/07/enum/ […]
Comment: #82
prototype has Object.keys(myobj) for this:
http://www.prototypejs.org/api/object/keys
Comment: #83
jPaq has this implemented the way that Mozilla suggested: http://jpaq.org/documentation/Array.prototype.forEach%28%29/1.0/
Comment: #84
[…] I’m just jumping into javascript but this post may help you. http://dean.edwards.name/weblog/2006/07/enum/ […]
Comments are closed.