如果没有具体的闭包知识,可以理解该片段:
function closure(){
var i = 1;
i++
var ofunc = function(){return i;};
return ofunc;
}
var x = closure()
x()
将返回:
=> 2
以下代码段非常违反直觉:
function closure(){
var i=1;
i++
var ofunc = function(){return i;};
i++;
return ofunc;
}
var x = closure()
x()
因为它将返回:
=> 3
尽管第二次增量操作(i++;
)在ofunc
之后,当ofunc
通过x
和x()
调用时,其范围是closure
{1}}。 closure
被执行,i
计算结果为3,因此结果。
一个明显的解决方案是:
function closure(){
var i = 1;
i++
var ofunc = function(){return i;}(); //note the parenthesis
i++;
return ofunc;
}
var y = closure()
y //note the missing parenthesis
再次回归更令人欣慰:
=> 2
在这种情况下,ofunc
在声明期间执行,因为最终尾随()
,使用当前值i
,2。因此y
没有' t返回指向ofunc
的指针,以y()
执行,但执行ofunc
的结果。
不幸的是,这会摧毁所有关闭的力量,或者至少在我看来。这是不能立即执行对象函数并在稍后执行时传递参数(到其指针)的可能性。实际上,如果我们向ofunc
添加参数:
function closure(){
var i = 1;
i++
var ofunc = function(j){return i+j;};
return ofunc;
}
我们现在可以调用链接的函数,如:
var x = closure()
x(10)
=> 12
此外,如果我们也向closure
添加参数,我们就可以创建一个参数ofunc
- 函数类,其中闭包的参数是ofunc
的参数。例如,如果ofunc
定义了一个对数,则闭包的参数可以设置为其基数,每个参数创建不同的基本对数运算,如:log10 = closure(10)
所以在我看来,()
- 形式实际上并不有趣。相反,最好通过获取闭包返回的函数的源来进行审计/调试,并使用外部参数评估。不幸的是:
x.toSource()
我们只获得未评估的表格:
=> '(function (j){return i+j;})'
如何在执行i
之后(但在执行closure
之前)获取x()
的实际值?那就是:
=> '(function (j){return 2+j;})'
答案 0 :(得分:1)
只需将您的变量复制到具有所需值的新变量:
function closure(){
var i = 1;
i++;
var i2 = i; /* Now i2 has desired value, even if you change i */
var ofunc = function(j){return i2+j;};
i++;
return ofunc;
}
var x = closure()
x(5);
另一种可能性:将您想要的i
作为参数传递给自动执行函数,该函数返回另一个函数:
function closure(){
var i = 1;
i++;
var ofunc = (function(i){ return function(j){return i+j;}; })(i);
i++;
return ofunc;
}
var x = closure()
x(5);
答案 1 :(得分:1)
理解上有一点差距。我将尝试解释这里发生了什么。
javascript关闭关闭结束/捕获引用到变量。这就是您在示例中遇到的情况。
function closure(){
var i=1;
i++
var ofunc = function(){
//JS Interpreter: Ohh, there's a variable called *i*, let me hold that reference.
return i;
};
ofunc(); //JS: Let me call that function.
//While executing: Hey, I have the reference to i, what's the value?
//Returns 2 here.
i++;
return ofunc;
}
closure()(); //JS: Let me call that function.
//While executing: Hey, I have the reference to i, what's the value?
//Value retriever(or something): The **latest** value is 3.
//Returns 3 here.
这是因为,闭包是捕获引用而不是值。如果需要捕获值,则需要以某种方式使闭包捕获变量的状态。 @Oriol描述了一种方式。另一种方法是将其包装在另一个闭包内,以便捕获它的参数。一个例子:
function closure(){
var i=1;
i++
var ofunc = function(x){return function(){return x;};}(i);
i++;
return ofunc;
}
closure()(); //returns 2, not 3! :-)
让我们剖析这一行:var ofunc = function(x){return function(){return x};}(i);
var ofunc = function(x){ // ** (i)
func = function(){
return x; //Reference to parameter passed in outer function.
};
}
result = ofunc(i); //is a function.
return result;
当您致电ofunc()
时,您会将参数传递给它。该参数表示为(i)。在函数体中,您创建新函数。这个函数是另一个闭包。这样做是因为它保留了对x
(来自参数)的引用。由于此x
是变量的状态,因此您实际执行的操作是:“创建捕获对外部函数中传递的参数的引用的函数”。传递的参数是变量的状态。因此,在调用ofunc
时,您创建一个 kinda 保存变量状态的函数。
为了理解实际的好处,让我们考虑以下用例:创建一个包含10个函数的数组,其中索引i
的每个函数都将获取一个输入参数,并在该输入参数乘以时返回结果i
。
案例#1 :(错误的做法!)
result = [];
for (var i=0; i<10; i++) {
func = function(x) {
return x*i;
};
result.push(func);
}
result[5](6); //Invoke the i-th function, expected result: 30, Actual result: 60!
案例#2 :(正确的方式)
result = [];
for (var i=0; i<10; i++) {
func = function(i_) {
return function(x){ return x*i_; };
};
result.push(func(i));
}
result[5](6); //result: 30