深入理解JavaScript系列:試着談談閉包


  閉包可能是JavaScript里最被人神乎其神的一個概念,世間萬物皆凡夫俗子,你覺着他神奇是因為你根本沒有了解,所有的事物當你了解透徹后就不會有這種不明覺厲的錯覺了。哈哈哈,上來又是一頓哲學普及。

  下面開始進行闡述。

  1.什么是閉包?

  參閱了很多資料,最后比較靠譜的解釋就是“閉包是一個函數”。

  閉包是一個函數,什么樣的函數才能叫做閉包?可以從他的作用上理解閉包就是能夠讀取其他函數內部變量的函數。

  2.閉包是怎么形成的?

  首先再回顧下JavaScript的作用域這個概念。JavaScript是函數作用域而不是塊狀作用域,也就是說JavaScript的作用域是以函數來划分的,函數內部的變量為私有變量,而相對的全局變量是指在所有的函數外部定義的變量。全局變量自動歸為全局對象global的屬性,在web瀏覽器中這個global對象由window對象來承擔這個全局變量的角色。

  一般的,在函數內部聲明的加了var聲明符的(函數內部定義變量不加var的話會默認聲明為全局變量,為了避免全局空間變量污染所以要謹記)變量,在這個函數執行時候創建該函數相關的作用域,函數當然會有嵌套,所以就會存在作用域鏈接作用域的問題,也就是所說的作用域鏈。這個作用域鏈從最里層函數作用域開始,逐漸向外層鏈接,一層一層直到最外層的全局作用域。所以在內層的函數使用某個變量的時候會按照這個鏈接順序去尋找,如果中途找到則停止,直到全局作用域還沒有找到則該變量為未定義。

  正常情況,一個函數在調用開始執行時創建這個函數執行上下文及相應的作用域鏈,在函數執行結束后釋放函數執行上下文及相應作用域鏈所占的空間。

  明白了作用域后再來看閉包怎樣才能讀取到其它函數的內部變量這個問題。首先,如果是函數A嵌套函數B,函數B自然可以訪問函數A里的變量,向上還可以直到全局作用域的變量。這種情況肯定形成不了閉包。所以閉包是在某函數A之外的函數B想要訪問函數A內部的變量下形成的。

  3.閉包怎么樣才能讀取到其它函數的內部變量

  最常見的形式就是在函數內部返回一個函數,這個返回的函數有引用外部函數的變量。示例代碼如下:

function foo() {
var tmp = 0;
return function () {
console.log(tmp = tmp + 1);
}
}
var bar = foo(); // foo()執行后返回內部匿名函數,所以bar現在就是內部的那個函數的引用
bar(); //現在執行bar,也就是執行內部的那個匿名函數,輸出為1,
bar(); //緊接着再執行bar,輸出為2,也就是說foo函數內部的tmp變量不僅被外部的函數訪問到了,而且tmp的值沒有被清空,會累加

  這就滿足了形成閉包的條件,即相對函數A(這里指foo())而言他外部的函數B(這里指bar(),其實bar只是內部匿名函數的引用在外部執行)訪問到了A作用域的變量tmp,而且值的注意的是tmp變量一直存在於內存中。

  依據上邊這段代碼解釋為什么變量會一直存在於內存中而不是隨着函數執行完畢后就被垃圾回收。

  首先,bar是在外部作用域中,大部分時候是在全局作用域中,而全局作用域中的變量是一直存在於內存中的,所以當bar引用foo的時候,間接地foo也會一直存在於內存中,既然foo都一直存在於內存中,那foo所形成的函數作用域里的一切都會一直存在於內存中,因此就會出現上邊看到的訪問到的內部變量不會隨着函數執行完后進行垃圾回收的情況。

  4.怎樣才能理解閉包?

  首先明白他是一個能訪問其它函數內部作用域變量的函數,打破了常規的函數作用域的限制,再理解就是他引用的變量會一直存在於內存中。

  下面貼一些閉包的代碼,都是從其他地方看到的:

var object = {
a: 1,
getA: function () {
console.log(this.a=this.a+1);
}
}
object.getA();//輸出為2
object.getA();//輸出為3

  

var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();

  5.閉包都有什么用處? 

  1)一個是前面提到的可以讀取函數內部的變量   2)另一個就是讓這些變量的值始終保持在內存中。   6.使用閉包需要注意的地方有哪些?   1)由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。   2)閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。     完!祝好運。

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com