ref –
- https://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/
- http://chineseruleof8.com/code/index.php/2015/08/05/self-invoking-anonymous-functions/
Every JavaScript function forms a closure on creation.
Thus, the add variable takes on a function closure. A closure is a function to which the variables of the surrounding context are bound by reference.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<!DOCTYPE html> <html> <body> <p>Counting with a local variable.</p> <button type="button" onclick="myFunction()">Count!</button> <p id="demo">0</p> <script> var add = (function () { console.log("beginning of add function"); var counter = 0; return function () { console.log("returning function..."); return counter += 1; } })(); function myFunction(){ document.getElementById("demo").innerHTML = add(); } </script> </body> </html> |
In our example, variable counter is declared within the function. And this variable is accessed via reference inside of our return function where counter is incremented.
Keep in mind that the return function acts as a template. When we press the button, the onClick event is passed.
Notice that the IIFE creates a closure and executes down the line: it logs, sets up variable counter to 0, then returns a function reference.
Hence, add is a reference to the returned function.
The returned function has a reference to counter. This is the closure.
All closures in javascript have a dictionary which it keeps track of all the things it has to reference.
When add executes
1 |
add() |
It will execute the inner function, and return counter + 1.
Problem Example
Let’s take this example.
1 2 3 4 |
fns = function(n) { console.log('inside inner function: i is: ' + i); return i+n; }; |
The function creates a closure, which keeps track of all variables at its outer lexical environment by using a dictionary.
n is passed in the parameter. But i passed through its lexical environment. So we keep track of it.
Say i is a loop’s index:
1 2 3 4 5 6 |
for (let i = 0; i < 4; i++) { fns[i] = (function(n) { console.log('inside inner function: i is: ' + i); return i+n; }); } |
when the loop is run, it assigns its elements like so:
fns[1] = (function(n)…
fns[2] = (function(n)…
fns[3] = (function(n)…
fns[4] = (function(n)…
When we execute the fns functions, the inner function is a closure with a dictionary for its lexical scope which references i.
For example when fns[1](7) is formed, it will be like so:
1 2 3 4 5 |
// i is 1, n is 7 fns[i] = (function(n) { console.log('inside inner function: i is: ' + i); return i+n; }); |
The situation is that the lexical environment dictionary will keep reference to i, which is 1 now. But when the loop goes to 2, the i here is 2. So on and so forth.
Let’s see what happens when we start executing these functions:
1 2 3 4 5 6 7 |
// all function closures are formed now formed var adders = createAdders(); // i in createAdders is now 4. // execute the 1-th indexed element of adders. Insert 7 as parameter. console.log(adders[1](7)); // i here is 4, parameter is 7 inside the function |
At the time of execution, i will be 4. And when the fns function executes, it will access i, and i will be 4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<!DOCTYPE html> <html> <body> <p>Counting with a local variable.</p> <p id="demo">0</p> <script> //Create an array of functions that add 1,2 and 3 respectively var createAdders = function() { var fns = []; //empty array to be used for (var i=1; i<4; i++) { fns[i] = (function(n) { console.log('inside inner function: i is: ' + i); return i+n; }); } return fns; } var adders = createAdders(); console.log('------ calling adders[]()-----------'); console.log(adders[1](7)); console.log(adders[2](7)); console.log(adders[3](7)); </script> </body> </html> |
Possible Solution
Since every function invocation takes place in a unique execution context, what we can do to fix the problem so that adders[1][7] = 8, is to execute our function at the time of form via IIFE:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var createAdders = function() { var fns = []; for (var i=1; i<4; i++) { // IIFE (function(i) { fns[i] = (function(n) { return i+n; }); })(i) } return fns; } |
Due to IIFE executing the function at the time of function creation, the i will be index.
This means that which at the time of assignment, whatever number is being used in the parameter of the function closure, is whatever i will be used in the return i + n.
thus, fns[1](7) = 1 + 7.
see here