js閉包的原理

什麼是閉包

從作用上說:既能重用一個變量,又能保護變量不會被全局污染的一種編程方式

閉包的本質/原理

外層函數的作用域對象,被內層函數引用着無法釋放,就形成閉包對象

要理解上面這兩句話首先我們來看一個小例子

<script>
      var a = 100;
      function fun() {
        a = 200;
        a++;
        console.log(a);
      }
      fun();
      console.log(a);
</script>

這個例子輸出的結果是什麼呢?以上面的例子來分析代碼執行的過程

  1. 當瀏覽器開始掃描時,這時候還沒有執行代碼。只是先聲明變量,將變量先存儲在window對象中(可打印一下console.log(window)看一看裏面的變量)
    在這裏插入圖片描述
    代碼執行後,函數會自動創建臨時函數作用域對象
    在這裏插入圖片描述
    當代碼執行完成後,會把臨時創建的函數作用域對象給清空
    在這裏插入圖片描述
    所以函數中的a是全局的變量a,所以兩次打印出來的值都是201。那如果把上面的例子改成下面的代碼會怎麼樣呢?
var a = 100;
      function fun() {
        var a = 200;
        a++;
        console.log(a);
      }
      fun();
      console.log(a);

這個例子輸出的結果是兩次201嗎?我們來輸出看一看
在這裏插入圖片描述
這爲什麼兩次的輸出不同呢?因爲函數裏面聲明瞭一個a變量,在函數裏面聲明的變量會存在臨時創建的函數作用域對象中,函數內部的變量a會先去臨時創建的函數作用域對象中查找,找到有a變量就拿函數作用域對象的變量來賦值,就不會去到window對象中找變量a,所以window對象中的a變量不受影響。
在這裏插入圖片描述
到代碼執行結束後臨時創建的函數作用域對象就會被回收,所以函數中的答打印是201,函數外的a變量沒有受到影響所以還是100
在這裏插入圖片描述
所以得出結論

1.使用全局變量:可以被重複使用,但是極易被污染和篡改。
2.使用局部變量:只能在函數內使用,出了函數用不了,絕對不會被篡改,但是不能重用。

那我想我的變量不被全局給污染和篡改又能重用應該怎麼辦?
這時候就要用到最上面閉包了,使用閉包就能實現這個需求,那閉包如何使用呢?看下面的例子

<script>
      //需求: 定義一個函數爲小孩兒管理壓歲錢
      //  小孩兒每花一筆錢,可以從總錢數中扣除花的錢,提示還剩xxx
      //1. 定義一個外層函數,包裹要保護的變量和內層函數
      function parent() {
        var total = 1000;
        //2. 外層函數將內層函數對象返回到函數外部,讓外部可用
        //不用給內層函數起名字
        return function (money) {
          //小孩兒 //局部
          total -= money;
          console.log(`花了${money}, 還剩${total}`);
        };
      }
      //3. 想用內層函數的人需要調用外層函數,才能獲得返回出來的內層函數,保存在變量中,反覆使用。
      var pay = parent();
      total = 0; //試圖篡改total變量 結果total不能被篡改
      pay(100); //花了100, 還剩900
      pay(100); //花了100, 還剩800
      pay(100); //花了100, 還剩700

      var pay1 = parent();
      pay1(100); //重新形成一個閉包 花了100, 還剩900
</script>

看看輸出結果
在這裏插入圖片描述
閉包的形成如下圖
在parent函數正在被調用的那瞬間,就會創建一個臨時函數作用域對象
在這裏插入圖片描述
然後調parent後,由於pay一直引用這parent創建的臨時函數作用域,導致這個臨時創建出來的函數作用域無法被釋放,而形成了閉包
在這裏插入圖片描述
總結成一句話

外層函數的作用域對象,被內層函數引用着無法釋放,就形成閉包對象

閉包的缺點

比一般的函數多佔用一塊內存空間——多佔外層函數的作用域對象

如何解決這個缺點呢?以上面的例子來說,如果pay不用到了,就將把pay = null,釋放掉函數作用對象。這樣就不會多佔外層函數的作用域對象

這是我個人對閉包的理解,如果說得不好請多多指教。

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