http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
Javascript語言的特殊之處,就在於函數內部可以直接讀取全局變量。
另一方面,在函數外部自然無法讀取函數內的局部變量。
二、如何從外部讀取局部變量?
出於種種原因,我們有時候需要得到函數內的局部變量。但是,前面已經說過了,正常情況下,這是辦不到的,只有通過變通方法才能實現。
那就是在函數的內部,再定義一個函數。
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
在上面的代碼中,函數f2就被包括在函數f1內部,這時f1內部的所有局部變量,對f2都是可見的。但是反過來就不行,f2內部的局部變量,對f1就是不可見的。這就是Javascript語言特有的"鏈式作用域"結構(chain scope),子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。
既然f2可以讀取f1中的局部變量,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內部變量了嗎!
關於上面的那種寫法還可以有這樣一種
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2();
}
f1()
eg:關於函數帶不帶括號之理解
get 是函數自身, get() 是運行函數
var x=get;
那么 x 和 get 是等同的
var x=get();
那么 x 是 函數 get 的返回值;
function get(){return 1}
var x=get;
alert(x)//function get(){return 1}
function get(){return 1}
var x=get();
alert(x)//1
function get(){return 1}
var x=get;
alert(x())//1
三、閉包的概念
上一節代碼中的f2函數,就是閉包。
各種專業文獻上的"閉包"(closure)定義非常抽象,很難看懂。我的理解是,閉包就是能夠讀取其他函數內部變量的函數。
由於在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成"定義在一個函數內部的函數"。
所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋梁。
五、使用閉包的注意點
1)由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
2)閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。
4.3.4 管理內存
使用具備垃圾收集機制的語言編寫程序,開發人員一般不必操心內存管理的問題。但是,JavaScript
在進行內存管理及垃圾收集時面臨的問題還是有點與眾不同。其中最主要的一個問題,就是分配給 Web
瀏覽器的可用內存數量通常要比分配給桌面應用程序的少。這樣做的目的主要是出於安全方面的考慮,
目的是防止運行 JavaScript 的網頁耗盡全部系統內存而導致系統崩潰。內存限制問題不僅會影響給變量
分配內存,同時還會影響調用棧以及在一個線程中能夠同時執行的語句數量。
因此,確保占用最少的內存可以讓頁面獲得更好的性能。而優化內存占用的最佳方式,就是為執行
中的代碼只保存必要的數據。一旦數據不再有用,最好通過將其值設置為 null 來釋放其引用——這個
做法叫做解除引用(dereferencing)。這一做法適用於大多數全局變量和全局對象的屬性。局部變量會在
它們離開執行環境時自動被解除引用,如下面這個例子所示:
function createPerson(name){
var localPerson = new Object();
localPerson.name = name;
return localPerson;
}
var globalPerson = createPerson("Nicholas");
// 手工解除 globalPerson 的引用
globalPerson = null;
在這個例子中,變量 globalPerson 取得了 createPerson() 函數返回的值。在 createPerson()
函數內部,我們創建了一個對象並將其賦給局部變量 localPerson ,然后又為該對象添加了一個名為
name 的屬性。最后,當調用這個函數時, localPerson 以函數值的形式返回並賦給全局變量
globalPerson 。由於 localPerson 在 createPerson() 函數執行完畢后就離開了其執行環境,因此
無需我們顯式地去為它解除引用。但是對於全局變量 globalPerson 而言,則需要我們在不使用它的
時候手工為它解除引用,這也正是上面例子中最后一行代碼的目的。
不過,解除一個值的引用並不意味着自動回收該值所占用的內存。解除引用的真正作用是讓值脫離
執行環境,以便垃圾收集器下次運行時將其回收。