搜尋

首頁  >  問答  >  主體

關於 javascript 閉包的問題?

為什麼變數 n 不會被重置?

function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000
  

說明變數 n 是個全域變量,是不是在 f2 中變數 n 被提升為了全域變數?

高洛峰高洛峰2827 天前986

全部回覆(9)我來回復

  • 给我你的怀抱

    给我你的怀抱2017-05-18 10:52:47

    因為js會為每個函式呼叫建立一個棧,函式內的函式也可以存取這個棧。

    首先你能調用nAdd,是因为你没加var,等于是在函数调用时定义了一个全局作用域下的nAdd,你加上var再這麼寫會報錯。

    你的var result=f1();调用了函数f1,也就创建了一个栈,保存了n=999,并返回了f2。之后你再怎么调用result(),其实都是在调用同一个f2,而这个f2引用的外部栈,自然还是第一次调用f1时候创建的那个。同样的nAdd雖然作用在全局,但存取的也是同一個堆疊內的資料。

    所以,並不是你說的因為nAdd是全域變量,所以n被提升成全域變量,而是nAdd所指向的函數和你回傳的那個閉包根本是在存取同一份資料。

    你可以嘗試改寫成

    function f1(){
        var n=999;
        nAdd=function(){n+=1}
        function f2(){
          alert(n);
        }
        return f2;
      }
    
      f1()(); // 调用f1,创建了一个栈,栈内n=999,创建了一个匿名函数,返回了一个闭包。
      nAdd(); // 调用了那个匿名函数
      f1()(); // 又调用f1,又创建了一个栈,栈内n=999,创建了另一个匿名函数,返回了另一个闭包。

    回覆
    0
  • 我想大声告诉你

    我想大声告诉你2017-05-18 10:52:47

    在這段程式碼中,result其實就是閉包f2函數。它總共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數f1中的局部變數n一直保存在記憶體中,並沒有在f1呼叫後被自動清除。
    為什麼會這樣呢?原因就在於f1是f2的父函數,而f2被賦給了一個全域變量,這導致f2始終在記憶體中,而f2的存在依賴f1,因此f1也始終在記憶體中,不會在呼叫結束後,被垃圾回收機制(garbage collection)回收。
    這段程式碼中另一個值得注意的地方,就是"nAdd=function(){n+=1}"這一行,首先在nAdd前面沒有使用var關鍵字,因此nAdd是一個全域變量,而不是局部變數。其次,nAdd的值是匿名函數(anonymous function),而這個匿名函數本身也是閉包,所以nAdd相當於一個setter,可以在函數外部對函數內部的局部變數進行運算。
    http://www.ruanyifeng.com/blo...

    回覆
    0
  • ringa_lee

    ringa_lee2017-05-18 10:52:47

    不是 n 被提升为全局变量了,这就是闭包。。。。
    nAdd是全局变量。
    nAddresult中涉及的n 都是 var n = 999那个n,而没有全局的n

    http://zonxin.github.io/post/...

    回覆
    0
  • 仅有的幸福

    仅有的幸福2017-05-18 10:52:47

    var nAdd = ... 你再試試你就知道為什麼了

    沒有var宣告 就會被提升為全域變數

    變數n不是全域變數 只是這樣寫讓這個n的記憶體總是無法釋放

    回覆
    0
  • 给我你的怀抱

    给我你的怀抱2017-05-18 10:52:47

    f2函數中對f1函數內的局部變數n持久引用,f2返回後,n不會被釋放,而nAdd作為一個全域函數,當然可以操作n

    回覆
    0
  • ringa_lee

    ringa_lee2017-05-18 10:52:47

    n還是局部變量,因為一直都是在函數f1中進行對局部變量n的操作。而nAdd()是一個全域下的函數,在他執行的時候,會將他所屬作用域中的n進行了加1

    回覆
    0
  • 阿神

    阿神2017-05-18 10:52:47

    • var result=f1(); 呼叫時,返回了一個內部函數f2,並且引用了外部函數的變數n,由於垃圾回收機制,f1被執行完畢的時候,。 n一直沒有被回收,result()執行第二次的時候,n變成了1000

    • nAdd 全域變量,沒有加var,所以是全域作用域

    • n 是局部變數

    回覆
    0
  • 黄舟

    黄舟2017-05-18 10:52:47

    1. var result=f1():f1函數回傳了f2函數
      把回傳的f2函數賦值給result全域變量,(f2的作用域鏈保存到result全域變數中)

    2. result():呼叫result(),這就形成閉包:有權訪問另外一個函數作用域中的變量
      因為在f2中的作用域引用了f1中的n這個局部變量,當f1執行完畢之後,垃圾回收機制發現n變數還在被result引用所以垃圾回收機制不會把n回收釋放。
      以至於n一直保存在result作用域鏈中。 result的作用域鏈正常能存取f1中的局部變數n,形成閉包。

    3. nAdd():nAdd沒有寫var所以nAdd是全域變量,在呼叫nAdd()和result()是一樣的都會形成閉包,匿名函數function(){n+=1}的作用域鏈中有n這個局部變量,所以當nAdd=funtion(){n+=1}時,這個匿名函數的作用域鏈保存到了全局變量nAdd形成閉包,調用nAdd()作用域鏈中找到f1局部變量n=999,n+ 1=1000。

    4. result():result()就輸出1000

    只是本人理解,如有錯誤請拍磚告知

    回覆
    0
  • PHP中文网

    PHP中文网2017-05-18 10:52:47

    n不是一個全域變量,這是一個閉包。為什麼n會改變,因為你的那個nAdd前面沒有寫var預設全域的,但是你的function是在閉包裡面定義的,作用域什麼的都是裡面的。

    回覆
    0
  • 取消回覆