更多技術分享,點擊右上角紅色的"關注",感謝你的支持!
閉包基本的概念
閉包並不是JavaScript特有的,在PHP、Scala、Groovy、Ruby、 Python、swift 以及Java(Java8及以上)等很多語言中,都有對閉包特性的支持。
簡單來說,閉包就是能夠讀取其他函數內部變量的函數。例如在javascript中,只有函數內部的子函數才能讀取局部變量,所以閉包可以理解成“定義在一個函數內部的函數“。
在本質上,閉包是將函數內部和函數外部連接起來的橋樑。
只看這些概念,是不是有點蒙圈了,別急,且往下看。
結合代碼來說明
如果一個函數a定義在另一個函數b裡面,那麼,這個函數a就是一個閉包。
重要的關鍵點,函數a能夠直接讀取函數b中定義的變量,這個就是閉包的特性
再來一段有意思的代碼例子:
函數b這次不再直接執行a了,而是直接返回了函數a,所以func本質上是對函數a實例的引用,
而func()竟然能記住x,這就是JavaScript的閉包機制的神奇之處。
到這裡,我們結合以上代碼說明,閉包就是函數a和變量x。
閉包可以用來做什麼
工作中,不知不覺,我們其實經常用到閉包,比如下面這個例子,一個初始化函數里麵包含一個按鈕點擊事件:
另一個具體點的例子,一段發送驗證碼的Demo:
可以看到,在onclick函數里面,是可以正常操作time_wait和time_left變量的,細心的朋友可能已經發現,
代理裡邊已經出現兩個閉包的應用了,一處是onclick,還有一處就是setInterval這個定時函數。
我們再想想,為什麼要用閉包?什麼場景需要呢?
JavaScript語言特性需要(setTimeout、setInterval...)
事件綁定需要(onclick...)
還有一個重要的原因,那就是OO思想,把上面發送驗證碼的代碼改造一下來看看
這樣,已經變成面向對象的編程風格了。
什麼時候不該用閉包
注意了朋友,不要為了秀技巧,到處使用閉包。因為濫用閉包可能會導致腳本執行緩慢,以及消耗不必要的內存。尤其是以下兩種情況時,應該儘量避免。
1.循環
頁面上測試,你會發現無論你點擊哪個按鈕,都會彈出4,因為click函數是個閉包,頁面加載後循環開始執行,當你點擊按鈕的時候,循環已經執行完了,此時i值為4,而閉包記得它周圍的變量,所以彈出的都是4。
要解決這個問題,可以把打印的代碼提煉到另一個函數里面,這樣就會產生一個新的作用域:
或者用立即執行函數表達式:
2.構造器
方法應該關聯於對象的原型,而不要定義到對象的構造器中。原因是這將導致每次對象實例化時,方法都會被重新定義一次。
應該這樣改寫,讓繼承的原型可以為所有實例共享:
結論
閉包就是一個函數引用另外一個函數的變量,因為變量被引用著所以不會被回收,因此可以用來封裝一個私有變量。這是優點也是缺點,不必要的閉包只會徒增內存消耗!另外使用閉包也要注意變量的值是否符合你的要求,因為他就像一個靜態私有變量一樣。閉包通常會跟很多東西混搭起來,接觸多了才能加深理解,這裡只是開個頭說說基礎性的東西。
閱讀更多 Java開發之路 的文章