As you begin to work more with JavaScript, you will inevitably come across 'oddities' when referencing variables; these issues are more than likely caused by scoping and or closure quirks. While scoping and closures are fundamental issues that have been discussed in great detail, there are some subtle nuances that are important to make note of.
I decided to write up this post after I bumped into a StackOverflow question which was asking a fairly common question regarding closures and scopes. While I understood the concept of the problem, I wanted to break it down a little further, and highlight a main conclusion:
Let's first describe a series of small examples that may lead you to a case where scoping issues will occur, and then how an anonymous function can save the day.
This is a very common thing to do:
var values = [];
for (var i=0; i < 5; i++) {
values.push(i);
}
console.log(values.toString());
>> 0,1,2,3,4
The array is populated with the values as expected. Since generating things with loops is fun, how about taking it a step further...
var funcs = [];
for (var i=0; i < 5; i++) {
funcs.push(function() { return i; });
// SPOILER! This does work the way you think it may.
}
Everything looks good right? Let's have a look by calling each of the functions to see their results:
var result = [];
for (var j=0; j < 5; j++) {
result.push(funcs[j]());
}
console.log(result.toString());
>> 5,5,5,5,5
Erm... wat?
Given the result in Example 1, you may assume that the result of Example 2 should also be 0,1,2,3,4, however, there is a subtle difference between the two examples causing this discrepancy.
In Example 1, we are pushing a primitive onto the array (i).
In Example 2, we are pushing an object onto the array (the function).
But we want the function to reference the value of i at the time it was created!
If we want to mimic the behaviour of Example 1, then we need to create a copy of i for each function being generated in the for loop. This is where anonymous functions can help!
An anonymous function is a function that has no name and is executed immediately:
(function() { /* code */ })();
Big deal right? At first glance this seems less than useful, however, we can make use of this construct to create a new scope. This gives us the ability to create the variable copy we so desire for Example 2:
var funcs = [];
for (var i=0; i < 5; i++) {
(function(k) {
funcs.push(function() { return k; });
})(i);
}
Here, we create an anonymous function which scopes the variable k and then immediately execute the function, passing in the primative i as the sole parameter. Each function now has access to a locally scoped variable k which stores the value of i at the time the annonomous function was executed. We can verify this with a test:
var result = [];
for (var j=0; j < 5; j++) {
result.push(funcs[j]());
}
console.log(result.toString());
>> 0,1,2,3,4
And there it is - each function returns us the result we desire!
by Shayla Sawchenko