js閉包是什麼?

1、閉包是什麼?

  閉包(closure)就是可以讀取其餘函數內部變量的函數。在javascript中,只有函數內部的子函數才能讀取局部變量,因此閉包能夠理解成 「定義在一個函數內部的函」。在本質上,閉包是將函數內部和函數外部鏈接起來的橋樑。(閉包的最典型的應用是實現回調函數(callback) )。javascript

2、JS中閉包的優缺點及特性

  → 優勢:

    1.保護函數內的變量安全
    2.在內存中維持一個變量(用的太多就變成了缺點,佔內存) ;
    3. 邏輯連續,當閉包做爲另外一個函數調用的參數時,避免你脫離當前邏輯而單獨編寫額外邏輯。
    4. 方便調用上下文的局部變量。
    5. 增強封裝性,能夠達到對變量的保護做用。java

  → 缺點:

    1.常駐內存,會增大內存使用量,使用不當很容易形成內存泄露。
    2.還有有一個很是嚴重的問題,那就是內存浪費問題,這個內存浪費不只僅由於它常駐內存,更重要的是,對閉包的使用不當會形成無效內存的產生。jquery

  → 特性:

    1. 函數嵌套函數
    2. 內部函數能夠訪問外部函數的變量
    3. 參數和變量不會被回收。web

3、變量做用域

  要理解閉包,僅理解上邊關於閉包的概念是不行的。首先要理解javascript的特殊的變量做用域。
  (1) 變量的做用域無非就兩種:全局變量和局部變量。
  (2) javascript語言的特別之處就在於:函數內部能夠直接讀取全局變量,可是在函數外部沒法讀取函數內部的局部變量。
  (3)注意點:在函數內部聲明變量的時候,必定要使用var命令。若是不用的話,你實際上聲明的是一個全局變量!安全

4、用代碼詮釋閉包

  在Javascript中閉包的建立過程,如如下程序所示。閉包

function a () {
   var i = 0;
   function b () {
      alert (i++);
   }
  return b;
}
var c = a();
c();  //函數調用
  →代碼特色

   這段代碼有兩個特色:
      一、函數b嵌套在函數a內部;
      二、函數a返回函數b。
   這樣在執行完var c = a( )後,變量c其實是指向了函數b,再執行c( )後就會彈出一個窗口顯示i的值(第一次爲1)。這段代碼其實就建立了一個閉包,這是由於函數a外的變量c引用了函數a內的函數b。也就是說,當函數a的內部函數b被函數a外的一個變量引用的時候,就建立了一個閉包。 框架

  → 做用

     簡而言之,閉包的做用就是在a執行完並返回後,閉包使得Javascript的垃圾回收機制不會收回a所佔用的資源,由於a的內部函數b的執行須要依賴a中的變量。
     在上面的例子中,因爲閉包的存在使得函數a返回後,a中的i始終存在,這樣每次執行c(),i都是自加1後alert出i的值。
     那麼咱們來想象另外一種狀況,若是a返回的不是函數b,狀況就徹底不一樣了。由於a執行完後,b沒有被返回給a的外界,只是被a所引用,而此時a也只會被b引 用,所以函數a和b互相引用但又不被外界打擾(被外界引用),函數a和b就會被回收。svg

  →應用場景

      一、保護函數內的變量安全。函數a中i只有函數b才能訪問,而沒法經過其餘途徑訪問到,所以保護了i的安全性。
      二、在內存中維持一個變量。因爲閉包,函數a中i的一直存在於內存中,所以每次執行c(),都會給i自加1。函數

5、如何從外部讀取函數內部的局部變量?

  出於種種緣由,咱們有時候須要獲取到函數內部的局部變量。可是,上面(3、變量做用域)已經說過了,正常狀況下,這是辦不到的!只有經過變通的方法才能實現。那就是在函數內部,再定義一個函數。性能

function demo1 () {
    var n = 6699;
    function demo2 () {
      alert(n); // 6699
    }
  }

  在上面的代碼中,函數 demo2 就被包括在函數demo1內部,這時demo1內部的全部局部變量,對demo2都是可見的。可是反過來就不行,demo2內部的局部變量,對demo1就是不可見的。
  這就是Javascript語言特有的」鏈式做用域」結構(chain scope),
  子對象會一級一級地向上尋找全部父對象的變量。因此,父對象的全部變量,對子對象都是可見的,反之則不成立。
  既然demo2能夠讀取demo1中的局部變量,那麼只要把demo2做爲返回值,咱們不就能夠在demo1外部讀取它的內部變量了嗎!

6、閉包的用途

  閉包能夠用在許多地方。它的最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中,不會在demo1調用後被自動清除。
  那爲何會這樣呢?緣由就在於demo1是demo2的父函數,而demo2被賦給了一個全局變量,這致使demo2始終在內存中,而demo2的存在依賴於demo1,所以demo1也始終在內存中,不會在調用結束後,被垃圾回收機制(garbage collection)回收。

7、使用閉包的注意點

  (1)因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。
  (2)閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數看成對象(object)使用,把閉包看成它的公用方法(Public Method),把內部變量看成它的私有屬性(private value),這時必定要當心,不要隨便改變父函數內部變量的值。

8、總結:

  1. 閉包是指有權訪問另外一個函數做用域中的變量的函數,建立閉包的最多見的方式就是在一個函數內建立另外一個函數,經過另外一個函數訪問這個函數的局部變量。閉包的缺點就是常駐內存,會增大內存使用量,使用不當很容易形成內存泄露。   2. 不適合場景:返回閉包的函數是個很是大的函數。     閉包的典型框架應該就是jquery了。     閉包是javascript語言的一大特色,主要應用閉包場合主要是爲了:設計私有的方法和變量。     這在作框架的時候體現更明顯,有些方法和屬性只是運算邏輯過程當中的使用的,不想讓外部修改這些屬性,所以就能夠設計一個閉包來只提供方法獲取。   3. 沒必要糾結到底怎樣纔算閉包,其實你寫的每個函數都算做閉包,即便是全局函數,你訪問函數外部的全局變量時,就是閉包 的體現。