Reputation: 42957
I am pretty new in JavaScript and I am studying the following topics: closure and IIFE (Immediately-invoked function expression).
So I have the following example related to the closure concept, it is very commented:
/* Classical example as far as why closure can make your code look hard to anticipate, but understanding how works is "simple"
*/
function buildFunctions() {
var arr = []; // Create an empty array
for (var i = 0; i < 3; i++) {
/* Add a new function to this array, identical functions but are 3 differents functions:
*/
arr.push(
function() { // It is not invoking the function, it is just creating it
console.log(i); // i= 0, 1, 2
}
)
}
return arr;
}
var fs = buildFunctions(); // Call the build() function that put the 3 functions inside the array
/* When these functions are actually invoked and it looks at the 'i' variable they use the OUTER REFERENCE inside their execution context. So what will be the values? You might expect that the values will be 0, 1, 2 but this is wrong because at the end of the for loop inside the buildFunctions() function the value of the 'i' variable is 3. So when the anonymous functions inside the arrays are invoked they use the OUTER REFERENCE and the CLOSURE CONCEPT to access to the memory space related to the popped of fbuildFunctions() function that contain the variables of this function, and here the 'i' value is 3 !!!
N.B: Many people think that the result should be 0,1,2 because they think that when the function is pushed into the array it is invoked but it is not !!! The function is invoked here:
*/
fs[0](); // Invoke the first function inside the array, the value is 3
fs[1](); // Invoke the second function inside the array, the value is 3
fs[2](); // Invoke the third function inside the array, the value is 3
/* What have I to do if I want this works? (I want that the first function return 0, the second return 1 and the third return 3).
In order to preserve the value of 'i' for the inner anonymous function I am going to need a separate execution context for each of the functions that I am pushing into the array. I need a parent scope that holds the current values of 'i' variable as the loop goes on. So, the only way to get an execution context is to execute a function. To execute a function on the fly I use IIFE concept.
In this way every time that the loop runs, differently from the previous example, the anonymous inner function is executed passing to it the current value of the 'i' variable. This value is so stored in the 'j' variable in the execution context of the current performed anonymous inner function.
So at first time pass 0, at the second time pass 1, at the third time pass 3 and these 3 values are stored in the 'j' variable of the related execution context.
*/
function buildFunctions2() {
var arr = []; // Create an empty array
for (var i = 0; i < 3; i++) {
arr.push(
(function(j) {
return function() {
console.log(j); // print the current value
}
}(i)) // I use IIFE concept to invoke the function passing the current value of the 'i' variable
)
}
return arr;
}
var fs2 = buildFunctions2(); // Call th build() function that put the 3 functions inside the array
/*
When these functions are performed don't use closure concept because it have not to use the outer reference but use the 'j' value inside the current function execution context.
*/
fs2[0]();
fs2[1]();
fs2[2]();
I have perfectly clear how the closure works but I have some doubts about the second example of the previous example, the one related to the buildFunctions2() function.
So the first example show the closure concept and the fact that, following the outer reference of each performed functions the program reach the memory space related to the variable of the buildFunctions() also if its execution context was popped off the execution context stack. So the result will be always the same for all the 3 functions, and it will be 3.
This is perfectly clear for me.
The second example intend to obtain the values 0, 1 and 3 when I perform the 3 functions putted inside the array.
To obtain this behavior the buildFunctions2() perform a for loop that put a function inside the current element of the array using the IIFE concept, in fact:
for (var i = 0; i < 3; i++) {
arr.push(
(function(j) {
return function() {
console.log(j); // print the current value
}
}(i)) // I use IIFE concept to invoke the function passing the current value of the 'i' variable
)
}
That, from what I have understand, it means that each time that enter in the for loop a new anonymous function is added to the array and performed passing to it the 'i' value as parameter. So in this way I am not using the closure concept because no my functions print the 'j' values that are in the context of these anonymous functions. Is it my reasoning correct?
The thing that I can't understand is: if the function is added to the array and performed, why have I also to add:
fs2[0]();
fs2[1]();
fs2[2]();
to obtain the result in the FireBug console? If I remove these 3 lines that invoke the function associated to all the elements of my array I obtain no result. This seems strange to me because, from what I have understand, using the IIFE, I add a function to the array and I automatically perform it passing to it the current 'i' value (that is 0,1,2) so why after have I to explicitly perform the functions?
What am I missing?
Upvotes: 0
Views: 549
Reputation: 17710
If you look at this part of the code:
(function(j) {
return function() {
console.log(j);
}
}(i))
You'll notice that there are two function
s. The outer one is an IIFE, and will be executed immediately when this part of the code is run.
What this outer function
does is define another function
(the inner one), which uses the value of j
that is local to the outer function (passed as a parameter). This function
, however, is not executed (like in the first example), and just returned, to be executed later when you run fs2[0]()
.
Examples of the different between definition and execution:
function fn1(a) { } // defines a function named fn1, does not execute it
fn1(123); // executes the function defined above
fn2 = function(a) { } // defines an anonymous function, does not execute it, then stores it in fn2
fn2(234); // executes the function defined above
(function(a) { } )(345); // defines an anonymous function, and executes it right away: that's an IIFE
Upvotes: 3