我是JavaScript的新手,我正在研究以下主题:闭包和 IIFE (立即调用函数表达式)。
所以我有以下与闭包概念相关的例子,非常评论:
/* 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]();
我非常清楚闭包是如何工作的,但我对前一个示例的第二个示例有疑问,该示例与 buildFunctions2()函数有关。< / p>
因此,第一个示例显示了闭包概念,以及在每个执行的函数的外部引用之后,程序到达与 buildFunctions()的变量相关的内存空间的事实/ strong>也可以从执行上下文堆栈中弹出执行上下文。因此,对于所有3个函数,结果将始终相同,并且它将为3。
这对我来说非常清楚。
当我执行在数组中放置的3个函数时,第二个示例意图获取值0,1和3.
要获得此行为, buildFunctions2()执行for循环,使用 IIFE 概念将函数放入数组的当前元素中,实际上:
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
)
}
根据我的理解,这意味着每次进入for循环时,新的匿名函数被添加到数组并执行传递给它的'i'值作为参数。因此,通过这种方式,我没有使用闭包概念,因为我的函数没有打印出这些匿名函数上下文中的'j'值。这是我的推理正确吗?
我无法理解的是:如果将函数添加到数组并执行,为什么还要添加:
fs2[0]();
fs2[1]();
fs2[2]();
在FireBug控制台中获取结果?如果我删除这3行调用与我的数组的所有元素相关联的函数,我得不到任何结果。 这对我来说似乎很奇怪,因为根据我的理解,使用 IIFE ,我向数组添加一个函数,然后我自动执行它,将当前的'i'值传递给它(即0, 1,2)那么为什么我要明确地执行这些功能呢?
我错过了什么?
答案 0 :(得分:3)
如果你看一下代码的这一部分:
(function(j) {
return function() {
console.log(j);
}
}(i))
您会注意到有两个function
。外部的是IIFE,并且在运行这部分代码时将立即执行。
这个外部function
做的是定义另一个function
(内部的),它使用外部函数本地的j
值(作为参数传递)。但是,此function
未执行(如第一个示例中所示),只是返回,稍后在您运行fs2[0]()
时执行。
定义和执行之间的不同示例:
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