閉包包含自由(未綁定到特定對象)變量;這些變量不是在這個代碼塊內(nèi)或者任何全局上下文中定義的访递,而是在定義代碼塊的環(huán)境中定義(局部變量)。"閉包" 一詞來源于以下兩者的結(jié)合:要執(zhí)行的代碼塊(由于自由變量被包含在代碼塊中,這些自由變量以及它們引用的對象沒有被釋放)和為自由變量提供綁定的計算環(huán)境(作用域)汽煮。在PHP、Scala棚唆、Scheme暇赤、Common Lisp、Smalltalk宵凌、Groovy鞋囊、JavaScript、Ruby瞎惫、 Python溜腐、Go、Lua瓜喇、objective c挺益、swift 以及Java(Java8及以上)等語言中都能找到對閉包不同程度的支持。
一句話可以概括:閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)乘寒,或者子函數(shù)在外調(diào)用望众,子函數(shù)所在的父函數(shù)的作用域不會被釋放。
- 變量作用域
一個變量的作用域(scope)是程序源代碼中定義這個變量的區(qū)域肃续。 全局變量擁有全局作用域黍檩,在javaScript代碼中任何地方都有定義的。 然而在函數(shù)內(nèi)聲明的變量只是在函數(shù)內(nèi)部有定義,他們是局部變量,作用域也只是在局部载城。
在函數(shù)體內(nèi),局部變量的優(yōu)先級要高于全局變量棵里。如果在函數(shù)體內(nèi)重新聲明一個與局部變量重名的變量润文,局部變量就會覆蓋全局變量的值。
在函數(shù)內(nèi)部聲明變量的時候殿怜,一定要使用var命令典蝌。如果不用的話,你實際上聲明的是一個全局變量头谜。 - 外部讀取函數(shù)內(nèi)部的局部變量
function f1 () {
var n = 999;
function f2 () {
alert(n);
}
return f2;
}
var result = f1();
result(); //999
- 內(nèi)存回收機(jī)制
JS的垃圾回收機(jī)制是為了以防內(nèi)存泄漏骏掀,內(nèi)存泄漏的含義就是當(dāng)已經(jīng)不需要某塊內(nèi)存時這塊內(nèi)存還存在著,垃圾回收機(jī)制就是間歇的不定期的尋找到不再使用的變量柱告,并釋放掉它們所指向的內(nèi)存截驮。
f1是f2的父函數(shù),而f2被賦給了一個全局變量际度,這導(dǎo)致f2始終在內(nèi)存中葵袭,而f2的存在依賴于f1,因此f1也始終在內(nèi)存中乖菱,不會在調(diào)用結(jié)束后坡锡,被內(nèi)存回收機(jī)制回收。
function f1 () {
var n = 999;
nAdd = function () {n+=1}
function f2 () {
alert(n);
}
return f2;
}
var result = f1();
result(); //999
nAdd();
result(); //1000
這段代碼中另一個值得注意的地方窒所,就是"nAdd=function(){n+=1}"這一行鹉勒,首先在nAdd前面沒有使用var關(guān)鍵字,因此nAdd是一個全局變量墩新,而不是局部變量贸弥。其次,nAdd的值是一個匿名函數(shù)海渊,而這個匿名函數(shù)本身也是一個閉包,所以nAdd相當(dāng)于是一個setter哲鸳,可以在函數(shù)外部對函數(shù)內(nèi)部的局部變量進(jìn)行操作臣疑。
- 閉包的用途
- 讀取函數(shù)內(nèi)部的變量
- 讓變量的值始終保持在內(nèi)存中,不會在f1調(diào)用后被自動清除
- 使用閉包需要注意的問題
- 由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中徙菠,內(nèi)存消耗很大讯沈,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題婿奔,在IE中可能導(dǎo)致內(nèi)存泄露缺狠。解決方法是,在退出函數(shù)之前萍摊,將不使用的局部變量全部刪除挤茄。
- 閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值冰木。所以穷劈,如果你把父函數(shù)當(dāng)作對象(object)使用笼恰,把閉包當(dāng)作它的公用方法,把內(nèi)部變量當(dāng)作它的私有屬性歇终,這時一定要小心社证,不要隨便改變父函數(shù)內(nèi)部變量的值。