setTimeout's Secret Free
Read the title how you wish, setTimeout
has a secret it has been holding onto for window.Infinity
milliseconds.
tl;dr: Solution 3.
The Problem
Because setTimeout
is defined globally on the window
object, the this
binding frequently becomes an issue. Case in point:
// Goal: print 0, 1...8
for( var i = 0; i < 9; i++ ) {
setTimeout(function() {
console.log( i );
}, 0);
}
Output:
Solution 1
for( var i = 0; i < 9; i++ ) {
setTimeout(function() {
console.log( this );
}.bind( i ), 0);
// bind context
}
Output:
In this example, rather than binding a real context, we bind the only thing that matters to us--the variable i
.
Now, why the weird PrimitiveValue output? You must bind to an object, not a primitive; as a result we bind to new Number( i )
.*
Solution 2
for( var i = 0; i < 9; i++ ) {
setTimeout(function( val ) {
console.log( val );
}.bind( null, i ), 0);
// bind with args
}
Output:
In this example, we don't care about the context thisArg at all. Whatever is passed into bind
after the thisArg becomes a parameter (val
), which is fed into the anonymous function.
Solution 3 (Personal Fave)
for( var i = 0; i < 9; i++ ) {
setTimeout(function( val ) {
console.log( val );
}, 0, i);
// Huh? The bind is gone!
}
Output:
A solution without bind
and the namesake for this post. setTimeout
doesn't just take a function and milliseconds as parameters, it also takes values which it will gladly pass to the function inside:
Further Reading:
- A quick review of JavaScripts's bind function can be found here.
- Read more about
setTimeout
at MDN. - Learn the difference between
new Number( 3 )
andNumber( 3 )
. - More on JavaScript primitives.
*Thanks to Andrew Teich for eloquently explaining binding, or lack-thereof, to primitives.
comments powered by Disqus