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