搜尋

首頁  >  問答  >  主體

javascript - 關於閉包的理解?求解惑。

function f1(){//2、找到 f1 函数,执行。
    var n=999;//3、给变量 n 赋值。
    nAdd=function(){n+=1}//9、找到 nAdd ,匿名函数内没有变量 n ,需要去上层查找,n = 999 +1。
    function f2(){//5、找到 f2 函数,执行。
        alert(n);//6、执行动作。
    }
    console.log(n);//新加上,测试,不参与执行步骤。
    return f2;//4、返回 f2 函数,需要寻找 f2 函数。
}
var result=f1();//1、将 f1函数的返回值赋值给 result 变量,result 也变成函数,需要寻找f1函数。
result(); //7、第一次执行 result 函数,将步骤 6 的执行动作(步骤 6)结果输出,n 等于 999。
nAdd();//8、执行 f1 函数里的全局变量函数 nAdd ,需要寻找 nAdd 函数。
result(); //10、第二次执行 result 函数,将步骤 5 的执行动作(步骤 6)结果输出,此时 n 等于 1000,因为第一次执行 result 函数时,查找了上层的作用域,n 是 999。
nAdd();//11、如果再执行 nAdd 函数,此时 nAdd 这个函数里的 n 是 1000,而 f1 函数的 n 还是 999,也就是说 f1 的变量 n 和 nAdd 的 n 是两个作用域不同的同名变量。
result(); 
f1();//新加上,测试

/*结果
控制台输出:999
弹窗:999
弹窗:1000
弹窗:1001
控制台输出:999
*/

想請教各位前輩看看這樣的理解是不是正確的。
補充:可不可以理解為,閉包第一次執行的時候需要往上層查找變量,找到之後,上層的變量值就變成了子函數的變量值,以後就不需要再去上一層查找,因為已經在第一次執行的時候繼承了,變成自己的。
感覺有點亂。 。 。
(摀臉

--------------------再補充--------------------
越看越亂。
然後徹底亂了。
從輸出結果來看,第一次控制台的輸出和最後一次控制台的輸出,f1的 n 是不可變的。
但是子函數之間不是不能互相讀取變數嗎?為什麼 nAdd 的表達式會影響到 f2 的 n?

高洛峰高洛峰2820 天前521

全部回覆(4)我來回復

  • 某草草

    某草草2017-05-18 10:51:50

    function f1(){
        var n=999;
        nAdd=function(){n+=1}
        function f2(){
            alert(n);
        }
        console.log(n);
        return f2;
    }
    var result=f1();
    result(); 
    nAdd();
    result();
    nAdd();
    result(); 
    var b = f1();//新加上,测试
    

    此時b和result中的n不是同一個
    b和nAdd的n是同一個.
    result的n已經無法改變.

    回覆
    0
  • PHP中文网

    PHP中文网2017-05-18 10:51:50

    有相同的問題,複製了下我的回答並添加一些。

    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

    5. nAdd();重複第三步驟 n+1 = 1001

    6. result();重複第四步驟 輸出n

    7. f1();調用f1時並沒有形成閉包,n永遠都是999,因為每次f1執行結束後n都被垃圾回收機制銷毀了,所以這裡再次調用var n=999;後面輸出也都是999

    為什麼 nAdd 的表達式會影響到 f2 的 n?
    因為閉包的原因n一直都沒被銷毀,nAdd()也形成了閉包,並改變了n的值,所以後面再次調用result() n沒被銷毀回收一直被+1,所以會被影響。
    最後在呼叫f1()時沒有閉包,之前n是被銷毀了。所以一直輸出a=999;

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

    回覆
    0
  • 黄舟

    黄舟2017-05-18 10:51:50

    建議你看下這個
    http://www.ruanyifeng.com/blo...
    上面解釋得比較清楚

    回覆
    0
  • 曾经蜡笔没有小新

    曾经蜡笔没有小新2017-05-18 10:51:50

    說的太複雜了吧

    這個閉包就是利用了js 的 static scope的特性實現的這個效果

    function f1(){
        var n=999;// 这里标识n 为代号 n1
        nAdd=function(){n+=1}+1。
        function f2(){
            alert(n);
        }
        console.log(n);
        return f2;
    }
    var result=f1();
    result(); 
    nAdd();
    result(); 
    nAdd();
    result(); 
    f1();
    

    第一次呼叫result():

    alert(n); 尋找的是 n1

    nAdd(); 增加的也是n1的值

    後面的也是這樣

    而最後一次 f1();

    運行的時候 var n = 999; 賦值的也是n1
    其中console.log(n) 的n 也是n1 所以印出的就是999

    你這個例子有點簡單,你可以看一個複雜的例子(重點看其中static scope)的說明:
    static scope

    回覆
    0
  • 取消回覆