想要學習閉包先來看看什麼是匿名函數吧!
(一)匿名函數
匿名函數就是沒有名字的函數。他有兩種聲明方式:
1.典型的函數聲明: function functionName(arg0,arg1,arg2){ //函數體 } 2.函數表達式: var functionName = function(arg0,arg1,arg2){ //函數體 } 雖然這兩種方式在邏輯上市等價的,可是他們仍是存在區別的。 區別1:前者會在代碼執行之前被加載到做用域中,然後者則是在代碼執行到那一行的時候纔會有意義。 區別2:前者會給函數指定一個名字,然後者則是建立一個匿名函數,而後將這個匿名函數賦給一個變量。 換句話說上面第二個例子:建立了一個帶有3個參數的匿名函數,而後把這個匿名函數賦給了變量functionName,並無給匿名函數指定名字。 (二)閉包 書上定義是這麼說的:指有權訪問另外一個函數做用域中的變量的函數 但是這種說法令新手難以理解。其實,在本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑。 1.那我先來講說爲何要有閉包這麼個概念吧,它產生的意義是什麼呢? (1)首先咱們學過前面的做用域了,知道了一個概念:函數內部能夠直接讀取全局變量。 那麼看代碼: var n=999; function f1(){ alert(n); } f1(); // 999 (2)而後另一個概念:在函數外部天然沒法讀取函數內的局部變量 那麼再看代碼: function f1(){ var n=999; } alert(n); //輸出錯誤 (3)這裏有一個地方須要注意,函數內部聲明變量的時候,必定要使用var命令。若是不用的話,你實際上聲明瞭一個全局變量!(咱們之前也提到過的!) function f1(){ n=999; } f1(); alert(n); // 999 下面關鍵的來了!:那就是如何從外部讀取局部變量呢? 那就是在函數的內部,再定義一個函數。(也就是閉包!!) function f1(){ var n=999; function f2(){ alert(n); // 999 } } 在上面的代碼中,函數f2就被包括在函數f1內部,這時f1內部的全部局部變量,對f2都是可見的。可是反過來就不行,f2內部的局部變量,對f1就是不可見的。既然f2能夠讀取f1中的局部變量,那麼只要把f2做爲返回值,咱們不就能夠在f1外部讀取它的內部變量了嗎!來看代碼: function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result = f1(); result(); //999 這段代碼與上面的不一樣點就是把f2函數做爲了一個返回值,而後在調用它。這時你確定在想最後兩行什麼意思啊?其實開始我也沒鬧明白,通過高人指點,其實這最後兩行的意思就是要調用f2這個函數的返回值。這兩行若是我改寫一下是否是更容易明白了呢? var result = f1(); result(); 合併成爲:f1()();其實結果是同樣的 也能夠更好的說明f2這個閉包的做用是:經過把它做爲返回值(由於它能訪問函數f1內的局部變量),而後從全局環境中調用這個返回值,這樣天然就達到了咱們的目的---從全局做用域中讀取局部函數內的變量! 2.既然知道了閉包的意義,下面就來了解下閉包的用途吧! (1)閉包的第一個用途,其實上面已經提到了,就是產生它意義:能夠讀取函數內部的變量 (2)閉包的第二個用途,那就是:可讓這些變量的值始終保持在內存中 第二個用途怎麼理解呢?來看代碼: function f1(){ var n=999; nAdd=function(){ n+=1 } function f2(){ alert(n); } return f2; } var result=f1(); //把f1函數的返回值(而這個返回值是函數f2的形式)給result result(); // 999 輸出這個f2的返回值 nAdd(); //調用nAdd函數 result(); // 1000 這裏就是閉包的第二個用途:f2這個閉包會讓變量n的值始終保存在內存中 光靠代碼來理解第二種用途,好像沒有什麼說服力,下面就用畫圖的方法來讓你們更深入的理解!! 第二種用途其實就和做用域鏈產生聯繫了,我來解釋下: 閉包f2從f1函數中被返回後,它的做用域鏈被初始化爲包含f1函數的活動對象和全局變量對象(黑線部分)。這樣f2就能夠訪問在f1()函數中定義的全部變量。更爲重要的是就算f1()被執行完畢後,它的活動對象也不會被銷燬,由於如圖f2這個閉包還在引用f1函數的活動對象,這也就是爲何上述第二種用途的緣由:閉包會讓變量始終保存在內存中,直到閉包被摧毀。