其實關于閉包各個論壇社群裡都有很多的文章來講它,畢竟閉包是JavaScript中一個特色,也正因為這個雨中不同的特色也讓閉包了解起來有一些吃力。筆者在這裡不僅僅是想介紹閉包,也向列舉一些筆者所見過的一些閉包,如果有讀者還有一些比較經典的閉包例子,希望可以在評論區裡留一下,謝謝。
說了半天,究竟什麼是閉包呢?
閉包就是函數的局部變量集合,隻是這些局部變量在函數傳回後會繼續存在。
閉包就是就是函數的“堆棧”在函數傳回後并不釋放,我們也可以了解為這些函數堆棧并不在棧上配置設定而是在堆上配置設定。
當在一個函數内定義另外一個函數就會産生閉包。
為了便于了解,我們可以簡單的将閉包了解為:
閉包:是指有權通路另外一個函數作用域中的變量的函數。
JavaScript中的作用域
JavaScript中是沒有塊級作用域的。不過關于塊級作用域我們在這裡不做深入探究,筆者在JavaScript的作用域和塊級作用域概念了解中有對塊級作用域較為詳細的解釋,不懂的讀者可以去看看。
如上函數,f1可調用全局變量n
另一方面,在函數外部自然無法讀取函數内的局部變量。
這裡有一個地方需要注意,函數内部聲明變量的時候,一定要使用var指令。如果不用的話,你實際上聲明了一個全局變量。
閉包
1.了解閉包
我們已經了解了什麼是作用域,什麼是塊級作用域,那又該如何去通路函數内部的變量呢?
出于種種原因,我們有時候需要得到函數内的局部變量。但是,前面已經說過了,正常情況下,這是辦不到的,隻有通過變通方法才能實作。
上面函數中的f2函數就是閉包,就是通過建立函數來通路函數内部的局部變量。
2.閉包的用途
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數内部的變量,另一個就是讓這些變量的值始終保持在記憶體中。
在這段代碼中,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,可以在函數外部對函數内部的局部變量進行操作。
3.閉包的注意點
1)由于閉包會使得函數中的變量都被儲存在記憶體中,記憶體消耗很大,是以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導緻記憶體洩露。解決方法是,在退出函數之前,将不使用的局部變量全部删除。
2)閉包會在父函數外部,改變父函數内部變量的值。是以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把内部變量當作它的私有屬性(private value),這時一定要小心,不要随便改變父函數内部變量的值。
4.經典閉包小案例
如果你能了解下面全部的案例,那你的閉包就算是真正掌握了。
//問:三行a,b,c的輸出分别是什麼?
這是一道非常典型的JS閉包問題。其中嵌套了三層fun函數,搞清楚每層fun的函數是那個fun函數尤為重要。
//答案:
//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1
都答對了麼?如果都答對了恭喜你在js閉包問題當中幾乎沒什麼可以難住你了。