2013-10-30 79 views
33

→ jsFiddle一個JavaScript封閉混亂

function f1(){ 
    var n=999; 

    nAdd=function(){n+=1;}; 

    function f2(){ 
     alert(n); 
    } 
    return f2; 
} 

var result = f1(); 
var result2 = f1(); 

result(); // 999 
nAdd(); 
result2(); // 1000 
result2(); // 1000 
result(); // 999 

我努力學習JavaScript關閉,但上述只是讓我困惑的代碼。 當第一次調用result()時,它是999.對我來說沒關係。

nAdd()之後被調用,result2()節目1000我認爲這是由於功能result2()和功能result()是平等的運作f1()

但爲什麼最後的result()顯示999而不是1000?

回答

36

每次調用f1()時,都會創建一個具有自己的本地變量n的新閉包。

然而,nAdd變量是全球性的,因此會被覆蓋每個f1()被調用的時候 - 這意味着呼籲nAdd()將只添加到n變量在最後關閉。

UPDATE:如果你希望能夠增加在每個封閉的n值獨立,你可以做這樣的事情:

function f1(){ 
    var n=999; 
    return { 
     incrementN : function(){n+=1;}, 
     getN : function f2(){console.log(n);} 
    } 
}  
var result = f1(); 
var result2 = f1(); 
result.getN(); // 999 
result.incrementN(); 
result2.getN();//999 
result2.incrementN(); 
result2.getN();//1000 
result.getN();//1000 

也就是說,有f1()返回一個包含兩個方法是不是對象聲明爲全局變量,並且它們都在它們所屬的閉包中的本地變量上運行。

4

resultresult2包含的f1不同調用的結果,並且因此含有局部變量n的不同實例。函數的每個調用對於該函數的局部變量可能具有不同的值。甚至在不涉及關閉時也適用。

+1

+1。每次輸入執行上下文時,都會創建一組新的變量。 – RobG

13

每次調用f1()你時間:

  • 創建的999
  • 的值稱爲n一個新的(本地)變量創建分配給全球nAdd的修改時n一個新的匿名函數(並覆蓋以前分配給nAdd的所有功能)
  • 創建一個新函數,返回哪個警報的值

您可以撥打f1()兩次,這樣你就可以做到兩次。第二次調用它時,用覆蓋nAdd的新函數修改第二個n

這留給你:

  • result()這提醒第一n
  • result2()這提醒第二n
  • nAdd()其遞增第二n

result()在最後一行提醒999,因爲它會提醒的第一n值(它從未遞增)。

+2

變量* nAdd *創建一次:第一次* f1 *在賦值語句被評估的位置被調用。之後,它只是每次調用* f1 *時改變的值。也許這只是說出你所說的話的另一種方式。 : -/ – RobG

1

nAdd=function(){n+=1;};行創建一個全局函數,它是f1()中的一個閉包。閉包也可以訪問創建它的函數範圍中的所有變量。因此,每次您撥打f1()時,都會創建一個新的nAdd()函數,其中n的值與f1()的調用值var n相關。

在你的代碼中;

var result = f1(); 
var result2 = f1(); 
result(); // 999 
nAdd();   // Created by "var result2 = f1();" and has the same 'n' value as function in result2 
result2();//1000 
result2();//1000 
result();//999 
0

結果和result2創建兩個不同的關閉與不同的n。如果您吶通過聲明它之外的F1()函數的全局變量,那麼你會得到你的預期,因爲在這種情況下,你總是會訪問全局變量n結果:

變種N = 999; function f1(){
nAdd = function(){n + = 1;};
function f2(){
console.log(n);
}
return f2;
}
var result = f1();
var result2 = f1();
result(); // 999
nAdd();
RESULT2(); // 1000
RESULT2(); // 1000
結果(); // 1000

27

目前已經是很好的答案,但我猜的圖片將是有益的理解。

enter image description here

+8

+1這是一個非常有創意的方式!最後點擊了 – ComFreek

+1

。感謝您的可視化 – Moak

+0

@Moak:不客氣。 –

0

它這樣:

var nAdd; 
function f1(){ 
    var n=999; 

    nAdd=function(){n+=1;}; 

    function f2(){ 
     alert(n); 
    } 
    return f2; 
} 

var result = f1();//var nAdd=function(){n+=1;} n=result.n=999 

var result2 = f1();//var nAdd=function(){n+=1;} n=result2.n=999 

var result3 = f1();//var nAdd=function(){n+=1;} n=result3.n=999 

nAdd(); 

result(); // 999 

result2(); // 999 

result3(); // 1000 

var result = f1();//var nAdd=function(){n+=1;} n=result.n=999 

var result2 = f1();//var nAdd=function(){n+=1;} n=result2.n=999 

nAdd(); 

var result3 = f1();//var nAdd=function(){n+=1;} n=result3.n=999 


result(); // 999 

result2(); // 1000 

result3(); // 999 

var result = f1();//var nAdd=function(){n+=1;} n=result.n=999 

var result2 = f1();//var nAdd=function(){n+=1;} n=result2.n=999 

nAdd(); 

var result3 = f1();//var nAdd=function(){n+=1;} n=result3.n=999 
nAdd(); 
nAdd(); 
nAdd(); 

result(); // 999 

result2(); // 1000 

result3(); // 1002 
+0

您是否願意向您的代碼解決方案添加解釋? –

+0

我猜這個傢伙顯示3個片段,每個片段都以代碼「result3();」結尾。然後可以理解。 – CoolGuy