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. 第二種方法更容易“移動”或概括。

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