so I didn’t need a self invoking foo to capture k ( in other words I thought the value was captured during the VAR declaration, the same way the magic constant ‘this’ is captured .
As Jeff Mott honed in on, no code inside a function ever executes until the function is called. Here is an example that I hope is not too insulting:
myjs.js
function greet() {
console.log("hello");
}
The confusion you are having with your code is actually the same as someone who asks, "How come I don’t see any output in the console when I run myjs.js?? Hopefully, your inner self is shouting, “That code won’t send anything to the console unless you add the line greet() !!”
Similarly, your anonymous onclick function doesn’t execute any of the code inside it until something is clicked. js does not care if there are 1,000 closures in your onclick function or if the end of the world is nigh–js will not under any circumstances even look at the code inside a function until you call the function. Okay, the parser does look inside the function to check for syntax errors and some other other things–but that’s it. Don’t believe me? Run this code:
myjs.js
function greet() {
var x = y/0;
}
No errors–yet clearly there are two errors: y does not exist, and there is a division by 0. So the question I pose to you is: if js won’t execute that assignment when the function is defined, why do you think js is going to show your function special consideration and execute the assignment:
var fn = fool;
when you define your onclick function?
Only after something is clicked will the code inside your onclick function execute. At that time, your assignment will execute, and js will ask itself, “What is the value of fool? I need to grab that value and make that assignment for this poor fellow!” And js does some checking and discovers that the variable fool is in scope(i.e. you don’t get an error for an undefined variable) because it was closed over by the onclick function. So js looks up the value of fool. The value of fool, like any variable, is the value that was last assigned to it.
There is no inconsistency with ‘this’, so you shouldn’t be confused about that either. For example, this line inside your onclick function:
var self=this;
does nothing and can be deleted.
Here are some simple examples to examine:
- A function closes over a variable–not a value:
function outer() {
var x = 10;
var func = function () {
console.log(x);
}
x = 11;
return func;
}
returned_func = outer();
//Time passes…
returned_func();
--output:--
11
a) When a function finishes executing, all local variables are destroyed.
b) x is a local variable of outer(), and normally x would be destroyed after the call to outer() finished executing.
c) However, because the anonymous function has x written inside it, x is not destroyed, and the anonymous function has access to x. In computer science speak, that state of affairs is indicated by saying, “the function closes over x”.
- There is no difference here:
function outer() {
var x = 10;
var func = function () {
var fn = x;
console.log(x);
}
x = 11;
return func;
}
returned_func = outer();
//Time passes…
returned_func();
--output:--
11
a) It’s easy to get fooled when looking at the assignment var fn = x, and think, "I see x up there, and x is 10, so fn is being assigned 10. Wrong!
b) The anonymous function has not executed yet, so no assignment takes place. The anonymous function does close over the variable x, which means that when the function executes it can access the variable x. And when the anonymous function executes, the value of x at that time will be assigned to fn.
- A twist:
function outer() {
var x = 10;
var y = x;
var func = function () {
console.log(y);
}
x = 11;
return func;
}
returned_func = outer();
//Time passes …
returned_func();
--output:--
10
a) y is assigned the current value of x which is 10. After that, y is never assigned to again, so it’s value remains 10.
b) When the inner function executes, it prints out the current value of y, which is 10.
There is a rule hiding in those examples: If a variable’s value will change several times before you call the inner function, like x in the examples, you can capture the current value in a variable, e.g. y, and write y in the inner function instead of the changing variable x.
–Note that the assignment takes place outside the inner function because the value you are interested in needs to be captured now–not when the inner function executes.
–Note that the rule also applies to ‘this’. ‘this’ is a constantly changing variable. Our code doesn’t explicitly change ‘this’, though, instead js secretly and behind our backs changes the value of the variable named ‘this’. So if at some point you have an inner function that needs to capture the current value of ‘this’, you can assign ‘this’ to a variable, e.g. y, and write y inside your inner function instead of ‘this’. Conceptually, there is no difference between a changing variable like x in the examples and ‘this’.
function outer() {
func_arr = [];
for(var i=10; i<13; ++i) {
func_arr.push( function () {
console.log(i);
});
}
return func_arr
}
funcs = outer();
for(var j=0; j<3; ++j) {
funcs[j]();
}
--output:--
13
13
13
If the previous examples have made any sense, then hopefully the reason for this example’s output is clear:
-
Each inner function closes over the variable i.
-
i increments to 13, which causes the for loop to terminate.
-
Later, when the anonymous functions execute they display the current value of i, which is 13.
-
Now, the trickiest part of closures in javascript. To remedy the problem in the previous example, let’s use the rule and capture the current value of i in a variable named y, and then write y inside the inner functions instead of the changing variable i:
function outer() {
func_arr = [];
for(var i=10; i<13; ++i) {
var y = i;
func_arr.push( function () {
console.log(y);
});
}
return func_arr
}
funcs = outer();
for(var j=0; j<3; ++j) {
funcs[j]();
}
--output:--
<not going to reveal it yet>
Any sane person would analyze what happens in that code like this:
a) Each time through the loop, a new variable y is declared.
b) Therefore, each inner function closes over a different y variable, and each of their y’s has a different value.
c) So the output, should be 10, 11, 12.
Wrong! The actual output is 12, 12, 12. Wtf?? That baffling output has to do with the way ‘var’ works. It turns out that this code:
function outer() {
for(var i=10; i<13; ++i) {
var y = i;
is equivalent to:
function outer() {
var y;
for(var i=10; i<13; ++i) {
y = i;
In other words, despite where you use var to declare a variable inside a function, the variable is global to the entire function. To keep from confusing yourself, you should declare all variables at the top of a function.
So because of the way var works, there is only one y variable, and all the inner functions close over the same variable; there is no way to get each of the inner functions to close over a different variable. Enter fretburner’s solution. Each time you call a function, new local variables are created, which solves the problem with the var declaration inside the for-loop not creating a new variable every time. (If you’ve understood everything so far, then you should be able to identify a line in the function that doesn’t do anything.)