JavaScript闭包与匿名函数

本文翻译自:JavaScript closures vs. anonymous functions

A friend of mine and I are currently discussing what is a closure in JS and what isn't. 我的一个朋友和我正在讨论什么是JS的封闭,什么不是。 We just want to make sure we really understand it correctly. 我们只是想确保我们真正理解它。

Let's take this example. 我们来看看这个例子吧。 We have a counting loop and want to print the counter variable on the console delayed. 我们有一个计数循环,并希望在控制台上打印计数器变量延迟。 Therefore we use setTimeout and closures to capture the value of the counter variable to make sure that it will not print N times the value N. 因此,我们使用setTimeout闭包来捕获计数器变量的值,以确保它不会打印N倍N值。

The wrong solution without closures or anything near to closures would be: 错误的解决方案,而闭合或接近闭合 ,以将任何东西:

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

which will of course print 10 times the value of i after the loop, namely 10. 这将当然打印的10倍的值i在循环后,也就是10。

So his attempt was: 所以他的尝试是:

for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i;
        setTimeout(function(){
            console.log(i2);
        }, 1000)
    })();
}

printing 0 to 9 as expected. 按预期打印0到9。

I told him that he isn't using a closure to capture i , but he insists that he is. 我告诉他,他并没有使用封闭来捕获i ,但他坚持认为他是。 I proved that he doesn't use closures by putting the for loop body within another setTimeout (passing his anonymous function to setTimeout ), printing 10 times 10 again. 我通过将for循环体放在另一个setTimeout (将他的匿名函数传递给setTimeout ),再次打印10次10​​来证明他没有使用闭包 The same applies if I store his function in a var and execute it after the loop, also printing 10 times 10. So my argument is that he doesn't really capture the value of i , making his version not a closure. 如果我将他的函数存储在var并在循环之后执行它同样适用,也打印10次10​​.所以我的论点是他并没有真正捕获 i的值 ,使他的版本不是一个闭包。

My attempt was: 我的尝试是:

for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2);
        }
    })(i), 1000);
}

So I capture i (named i2 within the closure), but now I return another function and pass this around. 所以我捕获了i (在闭包中命名为i2 ),但现在我返回另一个函数并传递它。 In my case, the function passed to setTimeout really captures i . 在我的例子中,传递给setTimeout的函数实际上捕获了i

Now who is using closures and who isn't? 现在谁在使用闭包,谁不是?

Note that both solutions print 0 to 9 on the console delayed, so they solve the original problem, but we want to understand which of those two solutions uses closures to accomplish this. 请注意,两个解决方案在控制台上打印0到9都会延迟,因此它们解决了原始问题,但我们想要了解这两个解决方案中的哪一个使用闭包来实现此目的。


#1楼

参考:https://stackoom.com/question/sFkm/JavaScript闭包与匿名函数


#2楼

After inspecting closely, looks like both of you are using closure. 仔细检查后,看起来你们两个都在使用闭合。

In your friends case, i is accessed inside anonymous function 1 and i2 is accessed in anonymous function 2 where the console.log is present. 在你的朋友的情况下, i在匿名函数1中访问,并且在存在console.log匿名函数2中访问i2

In your case you are accessing i2 inside anonymous function where console.log is present. 在您的情况下,您正在匿名函数中访问i2 ,其中存在console.log Add a debugger; 添加debugger; statement before console.log and in chrome developer tools under "Scope variables" it will tell under what scope the variable is. console.log之前的语句和在“范围变量”下的chrome开发人员工具中,它将告诉变量在什么范围内。


#3楼

According to the closure definition: 根据closure定义:

A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression). “闭包”是一个表达式(通常是一个函数),它可以将自由变量与绑定这些变量的环境 (“关闭”表达式)结合在一起。

You are using closure if you define a function which use a variable which is defined outside of the function. 如果定义一个使用在函数外部定义的变量的函数,则使用closure (we call the variable a free variable ). (我们将变量称为自由变量 )。
They all use closure (even in the 1st example). 它们都使用closure (即使在第一个例子中)。


#4楼

You and your friend both use closures: 你和你的朋友都使用闭包:

A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. 闭包是一种特殊的对象,它结合了两个东西:一个函数,以及创建该函数的环境。 The environment consists of any local variables that were in-scope at the time that the closure was created. 环境由创建闭包时在范围内的任何局部变量组成。

MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures MDN: https//developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures

In your friend's code function function(){ console.log(i2); } 在你朋友的代码函数function(){ console.log(i2); } function(){ console.log(i2); } defined inside closure of anonymous function function(){ var i2 = i; ... function(){ console.log(i2); }内的匿名功能的闭合定义function(){ var i2 = i; ... function(){ var i2 = i; ... and can read/write local variable i2 . function(){ var i2 = i; ...并且可以读/写局部变量i2

In your code function function(){ console.log(i2); } 在你的代码函数function(){ console.log(i2); } function(){ console.log(i2); } defined inside closure of function function(i2){ return ... and can read/write local valuable i2 (declared in this case as a parameter). function(){ console.log(i2); }内部函数关闭定义function(i2){ return ...并且可以读/写本地有价值i2 (在这种情况下,作为一个参数声明)。

In both cases function function(){ console.log(i2); } 在两种情况下函数function(){ console.log(i2); } function(){ console.log(i2); } then passed into setTimeout . function(){ console.log(i2); }然后传递到setTimeout

Another equivalent (but with less memory utilization) is: 另一个等价物(但内存利用率较低)是:

function fGenerator(i2){
    return function(){
        console.log(i2);
    }
}
for(var i = 0; i < 10; i++) {
    setTimeout(fGenerator(i), 1000);
}

#5楼

You are both using closures. 你们都在使用闭包。

I 'm going with the Wikipedia definition here: 我在这里使用维基百科的定义

In computer science, a closure (also lexical closure or function closure) is a function or reference to a function together with a referencing environment—a table storing a reference to each of the non-local variables (also called free variables) of that function. 在计算机科学中,闭包(也是词法闭包或函数闭包)是函数的函数或引用以及引用环境 - 存储对该函数的每个非局部变量(也称为自由变量)的引用的表。 A closure—unlike a plain function pointer—allows a function to access those non-local variables even when invoked outside of its immediate lexical scope. 闭包 - 与普通函数指针不同 - 允许函数访问那些非局部变量,即使在其直接词法范围之外调用时也是如此。

Your friend's attempt clearly uses the variable i , which is non-local, by taking its value and making a copy to store into the local i2 . 您朋友的尝试通过获取其值并将副本存储到本地i2 ,明确地使用变量i (非本地变量)。

Your own attempt passes i (which at the call site is in scope) to an anonymous function as an argument. 您自己的尝试将i (在调用站点的范围内)传递给匿名函数作为参数。 This is not a closure so far, but then that function returns another function that references the same i2 . 到目前为止,这不是一个闭包,但是该函数返回另一个引用相同i2函数。 Since inside the inner anonymous function i2 is not a local, this creates a closure. 由于内部匿名函数i2不是本地函数,因此会创建一个闭包。


#6楼

Let's look at both ways: 让我们看看两种方式:

(function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2);
    }, 1000)
})();

Declares and immediately executes an anonymous function that runs setTimeout() within its own context. 声明并立即执行在其自己的上下文中运行setTimeout()的匿名函数。 The current value of i is preserved by making a copy into i2 first; 通过首先复制到i2来保留i的当前值; it works because of the immediate execution. 它的工作原理是因为立即执行。

setTimeout((function(i2){
    return function() {
        console.log(i2);
    }
})(i), 1000);

Declares an execution context for the inner function whereby the current value of i is preserved into i2 ; 声明内部函数的执行上下文,其中i的当前值被保存到i2 ; this approach also uses immediate execution to preserve the value. 此方法还使用立即执行来保留值。

Important 重要

It should be mentioned that the run semantics are NOT the same between both approaches; 应该提到的是,两种方法之间的运行语义并不相同; your inner function gets passed to setTimeout() whereas his inner function calls setTimeout() itself. 你的内部函数传递给setTimeout()而他的内部函数调用setTimeout()本身。

Wrapping both codes inside another setTimeout() doesn't prove that only the second approach uses closures, there's just not the same thing to begin with. 将这两个代码包装在另一个setTimeout()并不能证明只有第二种方法使用闭包,开始时就不一样了。

Conclusion 结论

Both methods use closures, so it comes down to personal taste; 这两种方法都使用闭合,因此它归结为个人品味; the second approach is easier to "move" around or generalize. 第二种方法更容易“移动”或概括。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章