Part one, posted last week, explored a few different ways of de-cluttering your JavaScript, and, in the process, making it more readable.
Some developers don’t like the idea of using a language’s more idiosyncratic features because it does, potentially, make your code less readable in the eyes of those who haven’t learned the language properly. I think it’s still up for debate. While you’re pondering that, part II awaits…
If you haven’t checked out the “Truthy & Falsey” introduction then please do so before continuing.
Looping
It’s something we barely think about but JavaScript’s expressive diversity allows us to invent new patterns for looping. First, there’s the standard:
for (var i = 0; i < array.length; i++) {} |
This is conventional and will be known to developers coming from any language. There’s nothing inherently wrong with it. That said, for me, it’s not ideal.
The first improvement I usually make is to cache the length
property of the array:
for (var i = 0, l = array.length; i < l; i++) {} |
This doesn’t make it shorter, but it does make it more efficient, although marginally so…
Another change we can make is to combine the iteration expression (i++
) and the conditional expression (i < l
), like so:
for (var i = -1, l = array.length; ++i < l;) {} |
Notice that we’re starting with an index of -1
, but won’t worry because before the loop’s body runs, the conditional expression is run, and in the above code, the conditional expression is iterating the index and testing it against the length all in one.
It’s important to note that the prefix increment/decrement operators (++i
/--i
) return the changed value, while the postfix increment/decrement operators (i++
/i--
) return the old value. To make this clearer:
var x = 5; alert(x++); // alerts 5 (old value) alert(x); // alerts 6 var y = 5; alert(++y); // alerts 6 (new value) alert(y); // alerts 6 |
Another looping “pattern” in JavaScript is to assign and rely on what’s returned from that assignment for the conditional expression:
var elements = document.getElementsByTagName('a'); for (var i = -1, el; el = elements[++i];) { el; // => a single element } |
Yesterday’s post on truthy & falsey bears some relevance here. The expression el=elements[++i]
will return the value of elements[++i]
. If this value is falsey the loop will not continue. An element list is full of truthy values (objects). We don’t need to be worried about falsey values. Note that in this approach, you may be sacrificing performance for terseness.
In many situations, we do want to iterate over falsey values too, e.g.
var myArray = [1, 2, 3, null, 8, 54, null, 0, 32]; |
With something like this it would be best to use the traditional i<length
conditional expression, but if you’re so inclined feel free to mix the conditional and iteration expressions together like shown further up (++i<l
).
If we were feeling a bit crazy, we could combine everything — iteration, conditional test, and assignment of indexed value — in a single expression:
var array = ['a','b','c']; for (var i = -1, l = array.length, item; item = array[++i], i < l;) { alert(item); } // alerts, a, b, c |
Here we used the useful comma operator which always returns its right-hand operand. In this example we used it to make sure that i < l
is what the condition expression returns. Before the comma we’re assigning the value at the iterated index within the array.
I don’t really suggest doing all of this in one line, and to be honest, there’s nothing wrong with conventional looping structures. What I am trying to put across is how much expressive diversity JavaScript provides.
Assignments
Assignments are expressions too, so you can slot them in wherever you want:
var a = 1; alert(a = 2); // alerts 2 alert(a); // alerts 2 |
An assignment operator will always return whatever is on its right-hand side. Be careful with slotting in assignment expressions wherever you want though, because they can be misconstrued for equality operators. For example:
var match; if (match = '1/2/3'.match(/d/)) { //... } |
To some, upon initial inspection, it may appear that we’re testing match
for equality against '1/2/3'.match(/d/)
when in fact the latter is being assigned to the former, and the if
statement will run if the assignment expression returns a truthy value (i.e. if its right-hand side operand is truthy).
Casting to a number
Quick and easy:
var str = '222'; var num = Number(str); // => 222 typeof num; // => 'number' |
A shortcut is the unary plus (+
) operator, used like so:
var str = '222'; var num = +str; // => 222 typeof num; // => 'number' |
It works in exactly the same way.
Casting to a string
Once again, quick and easy:
var arr = [1,2,3]; var str = String(arr); // => '1,2,3' typeof str; // => 'string' |
The shortcut, this time, is to simply concatenate an empty string to the value that you’re casting:
var arr = [1,2,3]; var str = '' + arr; // => '1,2,3' typeof str; // => 'string' |
Saving references
Property lookup can be verbose and inefficient. To save space and to benefit performance it’s common to save references and access those properties via the new reference instead of having to evaluate the entire expression every time you want the nested value:
// Original: document.body.style.color; // "Caching" var style = document.body.style; // Whenever I need to access the `color` value: style.color; |
It’s often referred to as caching. Essentially, all that’s happening is that a new reference is being created for an object and being assigned to an identifier that you specify. Let’s start with this:
var data = { config: { doSomething: function(item){ alert(item); } } }; for (var i = -1, l = someArray.length; ++i < l;) { data.config.doSomething(someArray[i]); } |
It would make sense to minimise the amount of property access that needs to occur within the loop, and thus, potentially, anywhere else in our application that references data.config
too:
var config = data.config; |
Now the loop can simply reference config
instead of data.config
:
for (var i = -1, l = someArray.length; ++i < l;) { config.doSomething(someArray[i]); } |
We could even “cache” the function itself:
var doSomething = data.config.doSomething; |
And then call it directly within the loop:
for (var i = -1, l = someArray.length; ++i < l;) { doSomething(someArray[i]); } |
IMPORTANT: It’s important to note that, when you assign a member function (that is, a function that is a property of an object) you are de-binding its this
value, i.e. the context in which it runs. Here’s an example:
var obj = { value: 234, fn: function(){ return this.value; } }; obj.fn(); // => 234 var fn = obj.fn; fn(); // => undefined |
De-binding the fn
method will make its context the global (window) object instead of the obj
which we defined, so when our fn
function looks for its `this.value` it won’t find it (because this
is the window
, which doesn’t have `value` defined).
End of part 2
Comment if you feel compelled.
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
Best practical JavaScript article(s) in a long, long time.
Please, by all means, continue!
I really enjoyed. There is another shortcut for converting types. If you do
!!0
it returnsfalse
and!!1
returnstrue
.Very cool! A complete guide about casting and looping performance, I’ll definitely recommend this article.
I didn’t know the “looping on element” pattern, can’t wait for part 3 !
gracias por el trabajo james e esperado un rato pero a ciertamente valido la pena
Ooh… pretty interesting article. I think perhaps the for loop ideas are a step too far for me, but it’s interesting to read about them, nonetheless.
Keep ’em coming!
I’m not so sure about the looping optimisations. Shouldn’t the runtime know that array hasn’t been touched and optimise that away? It seems like your second guessing the VM.
Otherwise useful stuff.
I prefer using !1 for false and 1 for true, instead of !!0 and !!1. !!0 just seems like too much negation to read easily (and is extra bytes anyhow: if you really need those bytes so badly that you aren’t going to use true/false, might as well go all the way…) , and one vs. not one seems the easiest for someone reading the code to translate, if they’re not familiar with what you’re doing to save bytes.
See this SO question: http://stackoverflow.com/questions/5349425/whats-the-best-to-loop-an-array-in-javascript — be sure to see jondavidjohn’s updated answer which links to http://jsperf.com/caching-array-length/7
As it turns out some of the microoptimizations people got accustomed to using are in some cases substantially worse than just doing it straight up, for most modern browsers:
These forms are the slowest:
Who knew!
@Alex, @Jamie — the looping section was an attempt to show of JS’ expressive diversity. I don’t wish to dictate which looping structure is best. And I don’t usually care too much about micro(pre)-optimisations.
@Jamie, This loop seems to be the fastest in my checks:
That looping stuff is possible in most languages with C syntax. If you want to show off “expressive diversity”, something that exploits prototypal inheritance and higher-order functions would be better.
or the brain damaged version (honestly, I don’t know why I thought of this)
I’ve found very useful to use
~~
for casting everything to Integer…Thank you
In the last section (Saving references), I would always keep the binding of the function before assigning it to a variable:
@alpha123, I just use
myArray.forEach(function(){ /* ... */ })
. Most modern browsers will provide you with that method nowadays. I would assume it is the fastest way to loop through an array given thatArray.prototype.forEach
is native/underlying browser code. Don’t quote me on that, though.You don’t mention looping with the ‘in’ operator. I’m no expert so can’t say whether it is a good or a bad thing in terms of performance but it is nice and terse…
Of course using call or apply could help you both shorten the code and not lose the context.
For your last example you could do
Excellent article thanks – to be pedantic a function which is a member of an object is referred to as a method.
Thanks again. Darragh
Wouldn’t it be better to convert a string to number with –?
var str = ‘222’;
var num = –str; // => 222
typeof num; // => ‘number’
+ is used for string concatenation as well, so I don’t really know if it’s a good idea to use it?
@Garciat:
Yes, I know about forEach. The reason I wrote it that way was as an example to show the implementation (which definitely isn’t ES5 compliant), for his “expressive diversity” thing.
A regular for loop is always going to be faster than a forEach call. Method calls are expensive, the browser has to allocate a new function object, and the callback function is called for every element of the array. Compared to a low-level loop, it’s quite slow.
@Rob:
BAD IDEA.
This can be fixed by adding a hasOwnProperty check to the loops:
But you shouldn’t do that with arrays because it’s really slow. Even a forEach call is faster.
@Strx: That’s cute. I usually use + for coercion to a number, but that returns NaN for ‘a’, undefined, function () { }, etc (all of which ~~ returns 0 for). Still, + is going to be faster, and ~~ floors the operand (incorrectly no less, try ~~-1.2).
@Tom: -‘222’ returns -222. You probably want -(-‘222′) which behaves exactly the same as +’222’ (where ‘222’ can be anything).
@alpha123 – Do you mean using ‘in’ is really slow for looping arrays or adding a hasOwnProperty check is slow? I’m aware of the behaviour you demonstrated, hence the example including looping the properties of an object, and agree this is a bad idea to use as a matter of course. However the post is surrounding terseness and when for example looping an array of JSON data with a fixed predefined structure is there any real harm?
@Rob:
in
is really slow for looping Arrays (or anything else). See http://jsperf.com/for-loop-vs-for-in-loop. hasOwnProperty, which is basically required if you’re doing things that way, makes it even slower.Nice post man!!
It will be very useful this tuts for me!!
Just to complement, look some of my posts about Javascript Design Patterns
http://www.udgwebdev.com/design-patterns-para-javascript-parte-1/
http://www.udgwebdev.com/design-patterns-para-javascript-parte-2/
I know its in portuguese, but I’m very proud to be a brazilian! 😀
@Strx
Interesting trick. I never knew the tilde even did anything before. For anyone who’s wondering themselves, check out http://dreaminginjavascript.wordpress.com/2008/07/04/28/ for more info. I kinda wonder who came up with the idea for using the ~ like that because whoever (s)he is, (s)he probably should see a psychiatrist. 😉
Maybe I should do a short post about it on my blog, but I’m not 100% sure. We’ll see.
James,
One of the misleading things of
for(var i = 0; i < arr.length; i++) { }
is "i" is hoisted to the top of the object scope and not contained within the for loop. I would suggest declaring it outside the loop at the top as a general practice and then setting it like so:
// declared at top of object scope (function scope)
var i;
// … code
for(i = 0; i < arr.length; i++) { }
This is anal but its important for people not to misunderstand how JavaScript works as you are getting a lot of traffic so you have more responsibility to be a good role model. Lots of good stuff here by the way.