javascript
javascript闭包,你大爷永远是你大爷
閉包 what 's the fuck
閉包,是什么鬼,誰學誰都腦進水!
我會用5W3H1TS的方式,去講閉包
1. what
一個函數,記住自己定義時的“詞法作用域”,就產生了閉包
即使此函數在其他地方執行!
上一個“詞法作用域”的 例子 ,程序走起,與JAVA的動態作用域,有本質不同
因果鏈思維中,這個詞法作用域就是“已知的因為”,通過這個因為,我們往下想,才能想通,閉包這個結果
例子:
var fn1; function aa(){var t1 = 100;fn1 = function(){console.log(t1);} } var t1 = 200; aa(); fn1(); 復制代碼結果是打印 100,
結論: 可以確定,JS真的是詞法作用域
我是按“例子-結論”的思維去做的
2. why
閉包是解決函數式語言的一個問題的一種技術,這個問題就是如何保證將函數當做值創造并傳來傳去的時候函數仍能正確運行。
閉包實現的例子,其中一種用法: 模擬塊作用域,不要全局污染。因為JS只有全局作用域和函數作用域(這話并不完全對,但先記住,這就是JS讓人FUCK的原因。。。,不像JAVA,非常規矩,讓學的人,學起來也能非常清楚,省力)
3. who
JS有的,我們這里只討論JS的范疇,宿主環境,JS的引擎。其他語言,有的有,有的沒有
4.where
框架中,還有立即執行函數中,JQUERY庫,JQ寫的小組件
5. when
看下面鏈接的文章
6. how1
具體用時,要寫什么,先上一個小例子
例子:
var result = foo(1)(2); alert(result); function foo(a){return function(b){return a+b;} } 復制代碼很簡單,結果為3
再上一個有應用場景的小例子
<html> <title>小例子</title> <meta http-equiv="Content-Type" content="text/html" ; charset="utf-8"> <body onload="myEffect()"> <table id="mytab" border="1"><tr><td>第0行</td></tr><tr><td>第1行</td></tr><tr><td>第2行</td></tr> </table> <div id="console" style="background:#ffff00"></div> </body> <script>function myEffect(){var console=document.getElementById('console');var tab=document.getElementById('mytab');var trs=tab.getElementsByTagName('tr');for(var i=0;i<trs.length;i++){trs[i].onclick=(function(){var rowNum=i;return function(){console.innerHTML="點擊了第"+rowNum+"行";}})();}} </script> </html> 復制代碼試試,不去用 var rowNum=i;
而是改為:
trs[i].onclick=(function(){return function(){console.innerHTML="點擊了第"++"行";}})(); 復制代碼會發生神馬,然后自行體會一下,再往下看
7.how2
實現原理
函數調用是用棧,存變量是用堆(不一定正確,只是比喻,為了方便理解),
函數被調用時,函數
與內存GC機制相關,所以3時說,閉包,在其他語言中,有的有,有的沒有
一般來說,一個函數在執行開始的時候,會給其中定義的變量劃分內存空間保存,以備后面的語句所用,等到函數執行完畢返回了,這些變量就被認為是無用的了.對應的內存空間也就被回收了.下次再執行此函數的時候,所有的變量又回到最初的狀態,重新賦值使用.
但是如果這個函數內部又嵌套了另一個函數,而這個函數是有可能在外部被調用到的.并且這個內部函數又使用了外部函數的某些變量的話.這種內存回收機制就會出現問題.如果在外部函數返回后,又直接調用了內部函數,那么內部函數就無法讀取到他所需要的外部函數中變量的值了.所以js解釋器在遇到函數定義的時候,會自動把函數和他可能使用的變量(包括本地變量和父級和祖先級函數的變量(自由變量))一起保存起來.也就是構建一個閉包,這些變量將不會被內存回收器所回收,只有當內部的函數不可能被調用以后(例如被刪除了,或者沒有了指針),才會銷毀這個閉包,而沒有任何一個閉包引用的變量才會被下一次內存回收啟動時所回收.
這一段,即是原理,也是實現,(更是核心關鍵,理解了這個,就理解了閉包。)
用大白話,第一人稱的說話法是,我函數是大爺,我函數在定義時,所用的變量,你必須都給我找地方存好了,就存一份就行,爺兒我被調用的時候,就要用這些變量!記做:你大爺永遠是你大爺!補充:存的并不是這個變量聲明之時的快照,而是這些變量,在閉包函數執行時,變量中所存的真實值
函數的參數,在這個函數每次被調用之時,參數也是開出一個內存空間去存的
看個TIME的例子,也是核心例子了!
for(var i = 0; i < 5; i++) {setTimeout(function() {console.log(i);}, 1); //1毫秒后,打印} 復制代碼這個代碼,本意是想 打印
0
1
2
3
4
但是,打出來是
5
5
5
5
5
當你知道“你大爺永遠是你大爺”的理論后,就會明白,
function() {console.log(i); } 復制代碼此函數,所用的i這個變量,是存了一份,找地方放好了,但是這個變量i,他的值卻也在變化,所以當此函數用這個變量時,這個變量的值,就變為5了!
要想達到輸出,0到4的結果,就要讓變量存上一份,并且沒有其他程序去改變他,就OK了
下面,提供四個改法,都是用了上面的理論,大家再體會一下
for(var i = 0; i < 5; i++) {setTimeout((function(ii) {return function(){console.log(ii);}})(i), 1);} 復制代碼這是setTimeout內部的函數,去閉包存一份不變的變量法,還可以不用參數去存一份單獨的變量
for(var i = 0; i < 5; i++) {setTimeout((function() {var ii = i;return function(){console.log(ii);}})(), 1);} 復制代碼我們再來,把單存的變量,放到setTimeout之外
for(var i = 0; i < 5; i++) {(function(ii) {setTimeout(function () {console.log(ii);}, 1);})(i);} 復制代碼同理,也可以不用參數:
for(var i = 0; i < 5; i++) {(function() {var ii = i;setTimeout(function () {console.log(ii);}, 1);})();} 復制代碼這就是四個例子,核心思想就是讓閉包的變量是單獨的,不要被別的代碼去改變值,這樣就能達到想要的目的了,其實就是在內存中建了5個變量,去存這0,1,2,3,4這5個變量的值,在需要的時候,取出來用。
8.tip
優點:
1.保護函數內的變量安全,加強了封裝性
2.在內存中維持一個變量(用的太多就變成了缺點,占內存)
缺點
閉包的缺點就是常駐內存,會增大內存使用量,內存浪費,使用不當很容易造成內存泄露,無效內存的產生
9.scene
為了看 復雜的代碼時用,自己主動用得少,
藏于框架之中的有,交互響應,寫的函數中,傳的參數常量,就是閉包,但一般人就當黑盒子看,對使用框架的人來說,是透明的
id.onclick = function(){ alert(i); } 循環四次,打出來,都是4
用閉包,就搞定了
前端開發必須知道的JS(二) 閉包及應用
www.cnblogs.com/ljchow/arch…
PS : 沸水理論,簡版,一壺水,只有燒到100度才能去喝,你每次都只燒到80度就不燒了,那么你永遠也喝不上這水,學習也是一樣,快要學透閉包了,就停下來不學,那么永遠也不可能真正撐握閉包,不可能在工作中,隨心所欲的去使用閉包。
學到此,你應該已經是沸水100度了!
寫的不好,請大家多批評:)
總結
以上是生活随笔為你收集整理的javascript闭包,你大爷永远是你大爷的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java反射,代码优化
- 下一篇: NOIp 数据结构专题总结 (1):ST