通常函數(shù)內(nèi)部可以隨意使用全局變量,但無(wú)法從外部讀取函數(shù)內(nèi)部的變量,因此,我們需要在函數(shù)的內(nèi)部,再定義一個(gè)函數(shù)。
function f1( ){
? ? var n=999;
? ? function f2( ){ //函數(shù)f2被包括在f1內(nèi)部,這時(shí)f1內(nèi)部的所有局部變量,對(duì)f2都是可見(jiàn)的
? ? ? ? alert(n); // 999
? ? }
}
既然f2可以讀取f1中的局部變量,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內(nèi)部變量了嗎。
function f1(){
? ? var n=999;
? ? function f2(){
? ? ? ? alert(n);
? ? }
? ? return f2;
}
var result=f1();
result(); // 999
閉包的概念
上面例子中的f2函數(shù),就是閉包。
簡(jiǎn)單來(lái)說(shuō),閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
由于在Javascript語(yǔ)言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡(jiǎn)單理解成"定義在一個(gè)函數(shù)內(nèi)部的函數(shù)"。
在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來(lái)的一座橋梁。
閉包的五種寫(xiě)法
(1)定義函數(shù)Circle,給函數(shù)Circle添加一個(gè)area屬性

(2)聲明一個(gè)Circle變量,將一個(gè)函數(shù)當(dāng)作值賦給變量。(常用)

(3)new 一個(gè)對(duì)象(Object)為Circle,然后給對(duì)象添加屬性和方法。

(4)這種方法使用較多,也最為方便。var obj = {}就是聲明一個(gè)空的對(duì)象。(常用)

(5)不太常用

閉包的用途
(1)可以讀取函數(shù)內(nèi)部的變量。(如上面例子所說(shuō))
(2)讓這些變量的值始終保持在內(nèi)存中。
function f1( ){
? ? var n=999;
? ? nAdd= function(){n+=1}
? ? function f2(){
? ? ? ? alert(n);
? ? }
? ? return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在這段代碼中,result實(shí)際上就是閉包f2函數(shù)。它一共運(yùn)行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數(shù)f1中的局部變量n一直保存在內(nèi)存中,并沒(méi)有在f1調(diào)用后被自動(dòng)清除。
為什么會(huì)這樣呢?原因就在于f1是f2的父函數(shù),而f2被賦給了一個(gè)全局變量,這導(dǎo)致f2始終在內(nèi)存中,而f2的存在依賴(lài)于f1,因此f1也始終在內(nèi)存中,不會(huì)在調(diào)用結(jié)束后,被垃圾回收機(jī)制(garbage collection)回收。
這段代碼中另一個(gè)值得注意的地方,就是"nAdd=function(){n+=1}"這一行,首先在nAdd前面沒(méi)有使用var關(guān)鍵字,因此nAdd是一個(gè)全局變量,而不是局部變量。其次,nAdd的值是一個(gè)匿名函數(shù),而這個(gè)匿名函數(shù)本身也是一個(gè)閉包,所以nAdd相當(dāng)于是一個(gè)setter,可以在函數(shù)外部對(duì)函數(shù)內(nèi)部的局部變量進(jìn)行操作。
使用閉包的注意點(diǎn)
1)由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁(yè)的性能問(wèn)題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
2)閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。