在維基百科上對與閉包的理解是這樣的:閉包是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即便已經離開了創造它的環境也不例外。
在JavaScript中,咱們能夠這樣理解:當函數能夠記住並訪問所在的詞法做用域,即便函數是在當前的函數詞法做用域以外執行,這是就產生了閉包javascript
要想理解閉包,首先要知道做用域的概念java
做用域是根據名稱查找變量的一套規則,例如閉包
var a = 2
這個聲明,會有兩個過程,首先編譯時,編譯器會在當前做用於聲明這個變量(若是以前沒有聲明過的話),其次,引擎會詢問當前做用域是否有a這個變量,若是是,則將2賦值給它,若是否,則繼續查找這個變量函數
當一個塊或者一個函數嵌套在另外一個塊或函數裏,就產生了做用域嵌套,在當前做用域查找不到變量時會在向上查找,就比如,一個高樓,我要去一個朋友家裏,我要在第一層查找朋友家,可是若是沒找到,就要去上一層,若是到達了頂層還沒找到,咱們就不會在繼續查找了。code
咱們以前說過當函數能夠記住並訪問所在的詞法做用域,即便函數是在當前的函數詞法做用域以外執行,這是就產生了閉包
看一下下面的代碼對象
function foo(){ var a = 'dog'; function bar(){ console.log(a) } bar(); } foo();
咱們能夠看到bar函數可以訪問在foo函數裏定義的a,但這只是閉包的一部分,bar對a的引用,能夠看做是做用域的查找規則。則這些規則也是閉包的一部分。
那什麼是真正的閉包的?ip
function foo(){ var a = 'dog'; function bar(){ console.log(a); } return bar } var baz = foo(); baz(); //dog
咱們能夠看到,bar函數能夠訪問foo函數的做用域內部,以後咱們把bar自己看成返回值賦值給baz,經過baz的調用來執行bar函數,實際上就是根據不一樣的標識符來調用bar函數,達到在當前詞法做用域外執行的目的。
固然不管以何種方式對函數類型的值進行傳遞,讓函數在別處也能被調用到作用域
function foo(){ var name = 'dog'; function bar(){ console.log(name); } baz(bar); } function baz(fn){ fn(); }
咱們能夠經過來實現模塊編譯器
function module(){ var name = 'dog'; var age = 13; function SayName(){ console.log(name); } function SayAge(){ console.log(age); } return { SayName: SayName SayAge: SayAge } } var foo = module(); foo.SayName(); // dog foo.SayAge(); // 13
這個模式在javascript中被稱爲模塊
咱們來分析一下這個代碼,module函數返回一個對象,這個返回的對象保存了對內部函數而不是內部變量的引用,咱們保持對數據的隱私且私有的狀態,能夠將這個對象類型的返回值看做是一個公共API。這個返回的對象類型咱們賦值給了foo,以後經過foo來調用這個API的屬性訪問。SayAge()和SayName()函數具備涵蓋模塊實例內部做用域的閉包,當經過返回一個含有屬性引用的對象方式來說函數傳遞到詞法做用域外部時,就創造了觀察閉包的條件io