摘要:通過(guò)例子再來(lái)回顧一下閉包的理解所謂的閉包就是可以創(chuàng)建一個(gè)獨(dú)立的環(huán)境,每個(gè)閉包里面的環(huán)境都是獨(dú)立的,互不干擾。此時(shí)就是一個(gè)閉包,這樣寫(xiě)有些麻煩,我們對(duì)此改進(jìn)一下。參考文章初識(shí)中的閉包從閉
走在前端的大道上
本篇將自己讀過(guò)的相關(guān) javascript閉包 文章中,對(duì)自己有啟發(fā)的章節(jié)片段總結(jié)在這(會(huì)對(duì)原文進(jìn)行刪改),會(huì)不斷豐富提煉總結(jié)更新。
首先著重回顧一下 全局變量 和 局部變量 全局變量:可以在任意位置訪問(wèn)的量就叫 全局變量
var age = 20; function a(){ console.log(age); //20 } a();局部變量:函數(shù)中用var定義的變量,只能在函數(shù)中訪問(wèn)這個(gè)變量,函數(shù)外部訪問(wèn)不了。
function a(){ var age = 20; } a(); console.log(age); // Uncaught ReferenceError: age is not defined
重點(diǎn):
1.在函數(shù)中如果不使用var定義變量那么js引擎會(huì)自動(dòng)添加成全局變量。函數(shù)的相關(guān)知識(shí)點(diǎn):
2.全局變量從創(chuàng)建的那一刻起就會(huì)一直保存在內(nèi)存中,除非你關(guān)閉這個(gè)頁(yè)面,局部變量當(dāng)函數(shù)運(yùn)行完以后就會(huì)銷(xiāo)毀這個(gè)變量,假如有多次調(diào)用這個(gè)函數(shù)它下一次調(diào)用的時(shí)候又會(huì)重新創(chuàng)建那個(gè)變量,既運(yùn)行完就銷(xiāo)毀,回到最初的狀態(tài),簡(jiǎn)單來(lái)說(shuō)局部變量是一次性的,用完就扔,下次要我再重新創(chuàng)建。
1.一個(gè)函數(shù)內(nèi)可以嵌套多個(gè)函數(shù)
2.函數(shù)里面的子函數(shù)可以訪問(wèn)它上級(jí)定義的變量,注意不只是一級(jí),如果上級(jí)沒(méi)有會(huì)繼續(xù)往上級(jí)找,直到找到為止,如果找到全局變量到找不到就會(huì)報(bào)錯(cuò)。
function a(){ var name = "追夢(mèng)子"; function b(){ console.log(name); // "追夢(mèng)子" } b(); } a();
3.函數(shù)的另外一種調(diào)用形式,你可以把它叫做自調(diào)用,自己調(diào)用自己,達(dá)到自執(zhí)行的效果。
var a = 0; (function(){ console.log(++a); // 1 })()
這種方式用()把內(nèi)容包裹起來(lái),后面的()表示執(zhí)行這個(gè)函數(shù),可能你會(huì)問(wèn)為什么要把函數(shù)包起來(lái),如果不包裹起來(lái),js會(huì)把它當(dāng)作函數(shù)聲明來(lái)處理,如果包裹起來(lái)就是表達(dá)式。
正題閉包說(shuō)得通熟易懂一點(diǎn),就是指 有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域變量的函數(shù)。閉包可以解決函數(shù)外部無(wú)法訪問(wèn)函數(shù)內(nèi)部變量的問(wèn)題。創(chuàng)建閉包的常見(jiàn)方式,就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另外一個(gè)函數(shù),并返回。
下面是一段沒(méi)有使用閉包的代碼:
function fn(){ var a = 10; //報(bào)錯(cuò) } alert(a);
因?yàn)閍沒(méi)有定義,雖然函數(shù)fn里面定義了a但是,但是它只能在函數(shù)fn中使用。也就是作用域的問(wèn)題。
function fn(){ //定義了一個(gè)變量name var name = "追夢(mèng)子"; //外部想訪問(wèn)這個(gè)變量name怎么辦?return!把它返回出去,再用個(gè)變量接收一下不就可以了 return name; } var name = fn();//接收f(shuō)n返回的name值。 alert(name);//追夢(mèng)子;
這里的閉包就是利用函數(shù)的return。除了通過(guò)return還可以通過(guò)其他的幾種方法如下:
方法1:
function fn(){ var a = 0; b = a; } alert(b)
這里利用了js的一個(gè)特性,如果在函數(shù)中沒(méi)有用var定義變量,那么這個(gè)變量屬于全局的,但這種方法多少有些不好。
方法2:
var f = null; function fn(){ var a = 0; f = function(){ a++; f.a = a; }; } fn(); f(); alert(f.a);//1 f(); alert(f.a);//2
其實(shí)閉包還有一個(gè)很重要的特性,來(lái)看一個(gè)例子。
var lis= document.getElementsByTagName["li"]; //假如這段代碼中的lis.length = 5; for(var i=0;i最終結(jié)果是不管單擊哪個(gè)li元素都是彈5。不信你試試。為什么呢??聪旅娣治觥?/p>
for(var i=0;i為什么會(huì)這樣呢,因?yàn)槟鉬or循環(huán)只是給li綁定事件,但是里面的函數(shù)代碼并不會(huì)執(zhí)行啊,這個(gè)執(zhí)行是在你點(diǎn)擊的時(shí)候才執(zhí)行的好吧?但是此時(shí)的i已經(jīng)是5了,所以所有的都打印出5來(lái)了。
for(var i=0;i閉包的特點(diǎn)不只是讓函數(shù)外部訪問(wèn)函數(shù)內(nèi)部變量這么簡(jiǎn)單,還有一個(gè)大的特點(diǎn)就是 通過(guò)閉包可以讓函數(shù)中的變量持久保持。
function fn(){ var num = 5; num+=1; alert(num); } fn(); //6 fn(); //6為什么都是 6 呢?因?yàn)?函數(shù)一旦調(diào)用里面的內(nèi)容就會(huì)被銷(xiāo)毀,下一次調(diào)用又是一個(gè)新的函數(shù),和上一個(gè)調(diào)用的不相關(guān)了。
劃重點(diǎn):JavaScript中有回收機(jī)制,函數(shù)沒(méi)有被引用 且執(zhí)行完以后這個(gè)函數(shù)的作用域就會(huì)被銷(xiāo)毀,如果一個(gè)函數(shù)被其他變量引用,這個(gè)函數(shù)的作用域?qū)⒉粫?huì)被銷(xiāo)毀,(簡(jiǎn)單來(lái)說(shuō)就是函數(shù)里面的變量會(huì)被保存下來(lái),你可以理解成全局變量。)
再來(lái)
function fn(){ var num = 0; return function(){ num+=1; alert(num); }; } var f = fn(); f(); //1 f(); //2定義了一個(gè)fn函數(shù),里面有個(gè)num默認(rèn)為0,接著返回了一個(gè)匿名函數(shù)(也就是沒(méi)有名字的函數(shù))。我們?cè)谕獠坑?f 接收這個(gè)返回的函數(shù)。這個(gè)匿名函數(shù)干的事情就是把 num 加 1,還有我們用來(lái)調(diào)試的 alert 。
這里之所以執(zhí)行完這個(gè)函數(shù) num 沒(méi)有被銷(xiāo)毀,是因?yàn)槟莻€(gè)匿名函數(shù)的問(wèn)題,因?yàn)檫@個(gè)匿名函數(shù)用到了這個(gè) num,所以沒(méi)有被銷(xiāo)毀,一直保持在內(nèi)存中,因此我們 f() 時(shí) num 可以一直加。
function a(){ var aa = 0; function b(){ aa ++; console.log(aa); } return b; } var ab = a(); ab(); //1 ab(); //2里面的變量的值沒(méi)有被銷(xiāo)毀,因?yàn)楹瘮?shù)a被外部的變量ab引用,所以變量aa沒(méi)有被回收。
如果某個(gè)函數(shù)被它的父函數(shù)之外的一個(gè)變量引用,就形成了一個(gè)閉包
還有一種更為常用的閉包寫(xiě)法
var bi = (function(){ var a = 0; function b(){ a ++; console.log(a); } return b; })(); bi(); //1 bi(); //2 bi(); //3執(zhí)行過(guò)程分析:
首先把一個(gè)自執(zhí)行函數(shù)賦值給了bi,這個(gè)自執(zhí)行函數(shù)運(yùn)行完成以后就bi的值就變成了
function b(){ a ++; console.log(a); }因?yàn)槲覀冊(cè)谏厦娴拇a return 回去了 b,然后因?yàn)檫@個(gè)自執(zhí)行函數(shù)被 bi 引用所以里面的變量 a 并沒(méi)有因?yàn)檫@個(gè)自執(zhí)行函數(shù)執(zhí)完而銷(xiāo)毀,而是保存到了內(nèi)存中,所以我們多次打印 bi()
就成了1、2、3閉包是使用可以帶來(lái)以下好處:
希望一個(gè)變量長(zhǎng)期駐扎在內(nèi)存中
避免全局變量的污染
私有成員的存在
閉包可以讀取到函數(shù)內(nèi)部的變量,這是由于閉包后函數(shù)的堆棧不會(huì)釋放,也就是說(shuō)這些值始終保持在內(nèi)存中。這是一個(gè)優(yōu)點(diǎn),也是一個(gè)缺點(diǎn)。
通過(guò)例子再來(lái)回顧一下
閉包的理解:
所謂的閉包就是可以創(chuàng)建一個(gè)獨(dú)立的環(huán)境,每個(gè)閉包里面的環(huán)境都是獨(dú)立的,互不干擾。
閉包的創(chuàng)建:
一個(gè)函數(shù)中嵌套另外一個(gè)函數(shù),并且將這個(gè)函數(shù)return出去,然后將這個(gè)return出來(lái)的函數(shù)保存到了一個(gè)變量中,那么就創(chuàng)建了一個(gè)閉包。var arr = []; for(var i=0;i<2;i++){ arr[i] = function(){ console.log(i); } } arr[0](); //2 arr[1](); //2實(shí)際情況我們是要打印0,1,2,3這樣的數(shù),但是每次都是打印2,什么情況呢?雖然我們?cè)趂or中給arr的每個(gè)值添加了一個(gè)匿名函數(shù),但是 在for循環(huán)中我們并沒(méi)有執(zhí)行這個(gè)函數(shù),而是在for循環(huán)以后執(zhí)行的這個(gè)函數(shù),那么自然打印出來(lái)的就是for循環(huán)完以后i的值。
var arr = []; // for(var i=0;i<2;i++){ var i = 2; arr[0] = function(){ console.log(i); } arr[1] = function(){ console.log(i); } // } arr[0](); //2 arr[1](); //2相當(dāng)于這樣,雖然這個(gè)函數(shù)沒(méi)有執(zhí)行,但是arr的i已經(jīng)給執(zhí)行了,因?yàn)閍rr不是函數(shù)啊,肯定是執(zhí)行的,而你函數(shù)沒(méi)有調(diào)用自然就不會(huì)執(zhí)行,當(dāng)函數(shù)調(diào)用的時(shí)候i已經(jīng)是2了。既然如此只要我們?cè)趂or循環(huán)的時(shí)候直接執(zhí)行這個(gè)函數(shù)就ok。
var arr = []; for(var i=0;i<2;i++){ arr[i] = function(){ console.log(i); } arr[i](); } //0 //1這樣,在每一次for循環(huán)的時(shí)候就直接執(zhí)行了這個(gè)函數(shù),打印正常,但是我們這樣同樣有一個(gè)問(wèn)題,那就是在每一次for循環(huán)的時(shí)候這個(gè)函數(shù)就已經(jīng)被執(zhí)行了,我們要的是我們想什么時(shí)候調(diào)用就時(shí)候調(diào)用,而不是直接在for執(zhí)行中直接執(zhí)行,那么顯然這樣做是達(dá)不到我們的目的的。
現(xiàn)在我們?cè)谙胂腴]包的概念,閉包可以創(chuàng)建獨(dú)立的環(huán)境,并且每一個(gè)閉包的環(huán)境是獨(dú)立的,也就是說(shuō),我們可以通過(guò)閉包來(lái)保存這些不同的變量。
我們回顧一下閉包的創(chuàng)建方法:一個(gè)函數(shù)中嵌套另外一個(gè)函數(shù),并且將這個(gè)函數(shù)return出去,然后將這個(gè)return出來(lái)的函數(shù)保存到了一個(gè)變量中,那么就創(chuàng)建了一個(gè)閉包。
var arr = []; for(var i=0;i<2;i++){ arr[i] = a(i); } function a(i){ return function(){ console.log(i); } } arr[0](); //0 arr[1](); //1此時(shí)就是一個(gè)閉包,這樣寫(xiě)有些麻煩,我們對(duì)此改進(jìn)一下。
var arr = []; for(var i=0;i<3;i++){ arr[i] = (function(i){ return function(){ console.log(i); } })(i) } arr[0](); //0 arr[1](); //1 arr[2](); //2還可以這樣
var arr = []; for(var i=0;i<3;i++){ (function(i){ arr[i] = function(){ console.log(i); } })(i) } arr[0](); //0 arr[1](); //1 arr[2](); //2此時(shí) arr 里面的 i 用的是閉包里面的 i,而不是 for 中的 i,因?yàn)槲覀冋f(shuō)過(guò)每個(gè)閉包的環(huán)境都是獨(dú)立的。
js中的回收機(jī)制function a(){ var num = 10; return function(){ num ++; console.log(num); } } a()(); //11 a()(); //11按理說(shuō)第二次執(zhí)行函數(shù)a的時(shí)候應(yīng)該打印出12才對(duì),但是打印的卻是11。
首先來(lái)看看我們的理解
1.我們?cè)诤瘮?shù)a中返回了一個(gè)匿名函數(shù),在這個(gè)匿名函數(shù)中我們num++了一下,然后我們?cè)诤瘮?shù)外面執(zhí)行了這個(gè)匿名函數(shù)函數(shù),(第一括號(hào)執(zhí)行函數(shù)a第二個(gè)括號(hào)執(zhí)行這個(gè)rutrun回去的函數(shù))
2.現(xiàn)在num是11,然后我們又執(zhí)行了一次這個(gè)函數(shù),你們應(yīng)該是12吧,為什么不是呢?實(shí)際js的執(zhí)行
但是js的設(shè)計(jì)者為了讓沒(méi)有必要的變量保存在內(nèi)存中,(我們寫(xiě)的任何變量都是需要內(nèi)存空間的),什么叫沒(méi)有必要的變量?也就是說(shuō)你不在需要這個(gè)變量的時(shí)候它就會(huì)被銷(xiāo)毀?那么你肯定會(huì)問(wèn)js怎么知道那些變量是我們不需要的哪些是我們需要的。所以js為了知道哪些變量需要保存下來(lái),哪些不需要保存下來(lái),會(huì)進(jìn)行一些判斷。接下來(lái)我們就一起看看js是怎么判斷的。
1.在js中定義的全局變量是不會(huì)被銷(xiāo)毀的,因?yàn)槲覀冸S時(shí)都可能會(huì)用到這個(gè)變量,所以不能被銷(xiāo)毀。
2.但是在函數(shù)中定義的變量就不一定了,而且由于在函數(shù)的定義的變量的生命周期在執(zhí)行完這個(gè)函數(shù)就銷(xiāo)毀的原因自然就保存不了上一次的值。
3.但是并不是說(shuō)函數(shù)就真的保存不了上一次的值,因?yàn)橛械臅r(shí)候我們確實(shí)需要上一次的值,所以js判斷是否需要保存上一次變量的值的時(shí)候就會(huì)遵守這樣一個(gè)規(guī)則:
如果這個(gè)函數(shù)有被外部的變量引用就不會(huì)銷(xiāo)毀(這句話說(shuō)的不夠準(zhǔn)確,下面代碼會(huì)一步一步解釋?zhuān)?,否則銷(xiāo)毀。怎么理解這句話呢?
function a(){ var b = 0; return function(){ b ++; console.log(b); } } var d = a(); d();//1 d();//2函數(shù)a被變量d引用,更準(zhǔn)確的說(shuō)是函數(shù)a里面的那個(gè)匿名函數(shù)被變量d所引用,因?yàn)樽兞縟等于的是函數(shù)a執(zhí)行完成后的值,而函數(shù)a執(zhí)行完以后又因?yàn)楹瘮?shù)a返回了那個(gè)匿名函數(shù),所以準(zhǔn)確的說(shuō)是變量d等于匿名函數(shù)。而這個(gè)匿名函數(shù)因?yàn)槭褂昧撕瘮?shù)a中的變量b并且還被變量d所引用,所以就形成了一個(gè)閉包,只要這個(gè)變量d不等于null的話,那么那個(gè)變量b會(huì)一直保存到變量d中不會(huì)被銷(xiāo)毀。
總結(jié):
1、如果一個(gè)對(duì)象不被引用,那么這個(gè)對(duì)象就會(huì)被GC回收;
2、如果兩個(gè)對(duì)象互相引用,但是沒(méi)有被第3個(gè)對(duì)象所引用,那么這兩個(gè)互相引用的對(duì)象也會(huì)被回收。參考文章:
1.初識(shí)js中的閉包
2.從閉包案例中學(xué)習(xí)閉包的作用,會(huì)不會(huì)由你
3.那些年我們一起過(guò)的JS閉包,作用域,this,讓我們一起劃上完美的句號(hào)
4.再次講解js中的回收機(jī)制是怎么一回事。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/92071.html
摘要:引擎對(duì)堆內(nèi)存中的對(duì)象進(jìn)行分代管理新生代存活周期較短的對(duì)象,如臨時(shí)變量字符串等。內(nèi)存泄漏對(duì)于持續(xù)運(yùn)行的服務(wù)進(jìn)程,必須及時(shí)釋放不再用到的內(nèi)存。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第一期,本周的主題是調(diào)用堆棧,今天是第4天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)階計(jì)劃...
摘要:由于函數(shù)被調(diào)用了,線程會(huì)從剛剛保存的變量中取出內(nèi)容,去解析執(zhí)行它。最后,當(dāng)線程遇到離開(kāi)上下文的標(biāo)識(shí),便離開(kāi)上下文,并把的結(jié)果一并返回出去。 原文鏈接,歡迎關(guān)注我的博客 我相信很多前端初學(xué)者一開(kāi)始都會(huì)被執(zhí)行上下文這個(gè)概念弄暈,或者說(shuō)似懂非懂。對(duì)于工作兩年的我來(lái)說(shuō),說(shuō)來(lái)實(shí)在慚愧,雖然知道它大概是什么,但總覺(jué)得沒(méi)有一個(gè)更為清晰的認(rèn)識(shí)(無(wú)法把它的工作過(guò)程描述清楚),因此最近特意溫習(xí)了一遍,寫(xiě)下...
摘要:使用上一篇文章的例子來(lái)說(shuō)明下自由變量進(jìn)階期深入淺出圖解作用域鏈和閉包訪問(wèn)外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第7天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)階計(jì)...
摘要:前言函數(shù)式編程在前端已經(jīng)成為了一個(gè)非常熱門(mén)的話題。整個(gè)過(guò)程就是體現(xiàn)了函數(shù)式編程的核心思想通過(guò)函數(shù)對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換。高階函數(shù)函數(shù)式編程傾向于復(fù)用一組通用的函數(shù)功能來(lái)處理數(shù)據(jù),它通過(guò)使用高階函數(shù)來(lái)實(shí)現(xiàn)。 前言 函數(shù)式編程在前端已經(jīng)成為了一個(gè)非常熱門(mén)的話題。在最近幾年里,我們看到非常多的應(yīng)用程序代碼庫(kù)里大量使用著函數(shù)式編程思想。 本文將略去那些晦澀難懂的概念介紹,重點(diǎn)展示在 JavaScrip...
摘要:閉包面試題解由于作用域鏈機(jī)制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個(gè)值,這引起的一個(gè)副作用就是如果內(nèi)部函數(shù)在一個(gè)循環(huán)中,那么變量的值始終為最后一個(gè)值。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第8天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了...
閱讀 4411·2021-11-22 13:52
閱讀 2587·2021-11-22 13:52
閱讀 3744·2021-11-19 09:59
閱讀 1265·2021-11-17 09:33
閱讀 2519·2019-08-30 10:53
閱讀 1357·2019-08-29 17:28
閱讀 1362·2019-08-29 17:03
閱讀 3148·2019-08-26 11:31