來源于
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
http://www.cnblogs.com/xiaotie/archive/2011/08/03/2126145.html
看了幾個,覺得這兩篇博文寫的比較深刻且易懂。下面還是自己收藏整理了一份。
首先,我覺得,一個概念,如果不了解也不影響使用的話,那麼,就沒必要去了解它、去學習它。閉包就是這樣一個概念,你不了解它也能很好的用它。
下面是我了解的閉包概念。
先看看數學上的閉包。
(1,5) 是一個區間,但對這個區間做分析、計算什麼的,經常會用到1和5這兩個不屬于這個區間的值,[1,5]就是(1,5)的閉包。
在生活上,我們辦事情,找A部門,A部門說,你先得找B部門蓋個章,B部門說,你先得找C部門蓋個章,C部門說,這個東西不是我們的職權範圍…… 踢皮球,這就是非閉包。閉包就是負責到底,你找到A部門,A部門接待的那個人負責到底,他/她去協調B部門和C部門。
在工程上,閉包就是項目經理,負責排程項目所需要的資源。老闆、客戶有什麼事情,直接找項目經理即可,不用再去找其它的人。
在程式語言中,閉包就是一種文法糖,它以很自然的形式,把我們的目的和我們的目的所涉及的資源全給自動打包在一起,以某種自然、盡量不讓人誤解的方式讓人來使用。至于其具體實作,我個人意見,在不影響使用的情況下,不求甚解即可。在很多情況下,需要在一段代碼裡去通路外部的局部變量,不提供這種文法糖,需要寫非常多的代碼,有了閉包這個文法糖,就不用寫這麼多代碼,自然而然的就用了。
這樣一來,可以把閉包從一個文法機制提升為一種設計原則:
閉包是從使用者角度考慮的一種設計概念,它基于對上下文的分析,把龌龊的事情、複雜的事情和外部環境互動的事情都自己做了,留給使用者一個很自然的接口。
在這個原則下,函數式語言中,那種所謂的閉包隻是一種“閉包”,還有大量的其它類型的“閉包”等待發現和實作。
下面舉出一些閉包設計原則的正例和反例。
正例:
Flex中的資料綁定文法就是一種“閉包”。x=”{b.num + c.num}”,對于這個文法,編譯器自動去上下文中尋找叫 b 和 c 的變量,然後再找他們内部 num 變量,如果他們都是可綁定的話,則自動給它們添加上綁定鍊,當 b, c, num 等有任一變動時,更新 x 的值。
反例:
Winform 中的設計就違反了閉包原則,當不是在該UI線程中,更新某些控件的值時,會抛出異常。隻能去invoke調用,而invoke的接口很難用,相信很多人對這東東極其反感。
閉包不一定是文法糖。當我們不能直接擴充編譯器時,我們就無法增加文法糖來實作閉包機制,這時,就要用現有的語言機制來實作了。
閉包的用途
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數内部的變量,另一個就是讓這些變量的值始終保持在記憶體中。
怎麼來了解這句話呢?請看下面的代碼。
function f1(){
var n=;
nAdd=function(){n+=}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在這段代碼中,result實際上就是閉包f2函數。它一共運作了兩次,第一次的值是999,第二次的值是1000。這證明了,函數f1中的局部變量n一直儲存在記憶體中,并沒有在f1調用後被自動清除。
為什麼會這樣呢?原因就在于f1是f2的父函數,而f2被賦給了一個全局變量,這導緻f2始終在記憶體中,而f2的存在依賴于f1,是以f1也始終在記憶體中,不會在調用結束後,被垃圾回收機制(garbage collection)回收。
這段代碼中另一個值得注意的地方,就是”nAdd=function(){n+=1}”這一行,首先在nAdd前面沒有使用var關鍵字,是以nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(anonymous function),而這個匿名函數本身也是一個閉包,是以nAdd相當于是一個setter,可以在函數外部對函數内部的局部變量進行操作。
使用閉包的注意點
1)由于閉包會使得函數中的變量都被儲存在記憶體中,記憶體消耗很大,是以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導緻記憶體洩露。解決方法是,在退出函數之前,将不使用的局部變量全部删除。
2)閉包會在父函數外部,改變父函數内部變量的值。是以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把内部變量當作它的私有屬性(private value),這時一定要小心,不要随便改變父函數内部變量的值。
思考題
如果你能了解下面兩段代碼的運作結果,應該就算了解閉包的運作機制了。
代碼片段一
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
代碼片段二
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());