Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript closure and scope chain

Tags:

javascript

Can anyone explain why document.write part always outputs 10?

 function creatFunctions() {
     var result = new Array();
     for (var i = 0; i < 10; i++) {
         result[i] = function () {
             return i;
         }
     }
     return result;
 }
 var funcs = creatFunctions();

 for (var i = 0; i < funcs.length; i++) {
     document.write(funcs[i]() + "<br />");
 }

I think the answer to this question is more thorough than the duplicate question, therefore worth keeping this post.

like image 808
Blake Avatar asked Apr 21 '14 13:04

Blake


3 Answers

Sure.

In your for loop, you reference i. What you expect to be happening is that each closure gets a snapshot of i at the time the function is created, therefore in the first function it would return 0, then 1, etc.

What's really happening is that each closure is getting a reference to the external variable i, which keeps updating as you update i in the for loop.

So, the first time through the loop, you get a function that returns i, which at this point is 0. The next time you get two functions which return i, which is now 1, etc.

At the end of the loop, i==10, and each function returns i, so they all return 10.

UPDATE TO ADDRESS QUESTION IN COMMENT:

It's a little confusing since you use i in two different contexts. I'll make a very slight change to your code to help illustrate what's going on:

function creatFunctions() {
    var result = new Array();
    for (var i = 0; i < 10; i++) {
        result[i] = function () {
            return i;
        }
    }
    return result;
}
var funcs = creatFunctions();

// NOTE: I changed this `i` to `unrelated_variable`
for (var unrelated_variable = 0; unrelated_variable < funcs.length; unrelated_variable++) {
    document.write(funcs[unrelated_variable]() + "<br />");
}

... the functions that you create in your creatFunctions() function all return i. Specifically, they return the i that you create in the for loop.

The other variable, which I've renamed to unrelated_variable, has no impact on the value returned from your closure.

result[i] = function () {
    return i;
}

... is not the same thing as result[2] = 2. It means result[2] = the_current_value_of_i

like image 114
Jason Avatar answered Oct 24 '22 08:10

Jason


Because you reference i as a variable, which, when the function executes, has the value 10. Instead, you could create a copy of i by wrapping in in a function:

(function (i) {
    result[i] = function () {
        return i;
    }
}(i));
like image 36
jgillich Avatar answered Oct 24 '22 09:10

jgillich


The i that you are returning inside the inner function - is the i that was declared inside the for(var i=0...) therefor - it is the same i for all of the functions in result and by the time you are calling the functions its value is 10 (after the loop ends)

to accomplish what you wanted you should declare another variable inside the scope of the anonymous function

like image 3
Yaron U. Avatar answered Oct 24 '22 09:10

Yaron U.