關(guān)于閉包或渤,這些你理解嘛

今天看到了一段關(guān)于閉包的文章罢维,感覺挺好 挺有用的 我拿過(guò)來(lái)和大家分享一下,希望看到的小伙伴都能徹底理解閉包 => 提高JS水平 => 能夠?qū)懗龈哔|(zhì)量的JS代碼空闲。

我們開始吧:

相信眾多JS的lovers都聽說(shuō)過(guò)這句話:閉包很重要但是很難理解令杈。

我起初也是這么覺得,但是當(dāng)我努力學(xué)習(xí)了JS的一些深層的原理以后我倒覺得閉包并不是那么不好理解碴倾,反倒是讓我感覺出一種很美的感覺逗噩。當(dāng)我徹底理解閉包的那一剎那,心中油然產(chǎn)生一種十分愉悅感覺跌榔,就像"酒酣尚醉异雁,花未全開"那種美景一樣。

撥開閉包神秘的面紗

我們先看一個(gè)閉包的例子:

function foo() {

let a = 2;


function bar() {

    console.log( a );

}


return bar;

}

let baz = foo();

baz();

大家肯定都寫過(guò)類似的代碼僧须,相信很多小伙伴也知道這段代碼應(yīng)用了閉包纲刀,but, Why does the closure be generated and Where is closure?

來(lái),我們慢慢分析:

首先必須先知道閉包是什么担平,才能分析出閉包為什么產(chǎn)生和閉包到底在哪示绊?

當(dāng)一個(gè)函數(shù)能夠記住并訪問(wèn)到其所在的詞法作用域及作用域鏈,特別強(qiáng)調(diào)是在其定義的作用域外進(jìn)行的訪問(wèn)驱闷,此時(shí)該函數(shù)和其上層執(zhí)行上下文共同構(gòu)成閉包耻台。

需要明確的幾點(diǎn):

閉包一定是函數(shù)對(duì)象(wintercn大大的閉包考證)

閉包和詞法作用域空免,作用域鏈空另,垃圾回收機(jī)制息息相關(guān)

當(dāng)函數(shù)一定是在其定義的作用域外進(jìn)行的訪問(wèn)時(shí),才產(chǎn)生閉包

閉包是由該函數(shù)和其上層執(zhí)行上下文共同構(gòu)成(這點(diǎn)稍后我會(huì)說(shuō)明)

閉包是什么蹋砚,我們說(shuō)清楚了扼菠,下面我們看下閉包是如何產(chǎn)生的。

接下來(lái)坝咐,我默認(rèn)你已經(jīng)讀過(guò)我之前的兩篇文章 原來(lái)JavaScript內(nèi)部是這樣運(yùn)行的 和 徹底搞懂JavaScript作用域 , 建議先進(jìn)行閱讀理解JS執(zhí)行機(jī)制和作用域等相關(guān)知識(shí)循榆,再理解閉包,否則可能會(huì)理解的不透徹墨坚。

現(xiàn)在我假設(shè)JS引擎執(zhí)行到這行代碼

let baz = foo();

此時(shí)秧饮,JS的作用域氣泡是這樣的:

這個(gè)時(shí)候foo函數(shù)已經(jīng)執(zhí)行完,JS的垃圾回收機(jī)制應(yīng)該會(huì)自動(dòng)將其標(biāo)記為"離開環(huán)境",等待回收機(jī)制下次執(zhí)行泽篮,將其內(nèi)存進(jìn)行釋放(標(biāo)記清除)盗尸。

但是,我們仔細(xì)看圖中粉色的箭頭帽撑,我們將bar的引用指向baz泼各,正是這種引用賦值,阻止了垃圾回收機(jī)制將foo進(jìn)行回收亏拉,從而導(dǎo)致bar的整條作用域鏈都被保存下來(lái)扣蜻。

接下來(lái)逆巍,baz()執(zhí)行,bar進(jìn)入執(zhí)行棧莽使,閉包(foo)形成锐极,此時(shí)bar中依舊可以訪問(wèn)到其父作用域氣泡中的變量a。

這樣說(shuō)可能不是很清晰芳肌,接下來(lái)我們借助chrome的調(diào)試工具看下閉包產(chǎn)生的過(guò)程溪烤。

當(dāng)JS引擎執(zhí)行到這行代碼let baz = foo();時(shí):

圖中所示,let baz = foo();已經(jīng)執(zhí)行完庇勃,即將執(zhí)行baz();檬嘀,此時(shí)Call Stack中只有全局上下文。

接下來(lái)baz();執(zhí)行:

我們可以看到责嚷,此時(shí)bar進(jìn)入Call Stack中鸳兽,并且Closure(foo)形成。

針對(duì)上面我提到的幾點(diǎn)進(jìn)行下說(shuō)明:

上述第二點(diǎn)(閉包和詞法作用域罕拂,作用域鏈揍异,垃圾回收機(jī)制息息相關(guān))大家應(yīng)該都清楚了

上述第三點(diǎn),當(dāng)函數(shù)baz執(zhí)行時(shí)爆班,閉包才生成

上述第四點(diǎn)衷掷,閉包是foo,并不是bar柿菩,很多書(《you dont know JavaScript》《JavaScript高級(jí)程序設(shè)計(jì)》)中戚嗅,都強(qiáng)調(diào)保存下來(lái)的引用,即上例中的bar是閉包枢舶,而chrome認(rèn)為被保存下來(lái)的封閉空間foo是閉包懦胞,針對(duì)這點(diǎn)我贊同chrome的判斷(僅為自己的理解,如有不同意見凉泄,歡迎來(lái)討論)

閉包的藝術(shù)性

我相信這個(gè)世界上最美的事物往往就存在我們身邊躏尉,通常它并不是那么神秘,那么不可見后众,只是我們?nèi)鄙倭艘浑p發(fā)現(xiàn)美的眼睛胀糜。

生活中,我們抽出一段時(shí)間放慢腳步蒂誉,細(xì)細(xì)品味我們所過(guò)的每一分每一秒教藻,會(huì)收獲到生活給我們的另一層樂(lè)趣。

閉包也一樣拗盒,它不是很神秘怖竭,反而是在我們的程序中隨處可見,當(dāng)我們靜下心來(lái)陡蝇,品味閉包的味道痊臭,發(fā)現(xiàn)它散發(fā)出一種藝術(shù)的美哮肚,樸實(shí)、精巧又不失優(yōu)雅广匙。

細(xì)想允趟,在我們作用域氣泡模型中,作用域鏈讓我們的內(nèi)部bar氣泡能夠"看到"外面的世界鸦致,而閉包則讓我們的外部作用域能夠"關(guān)注到"內(nèi)部的情況成為可能潮剪。可見分唾,只要我們?cè)敢饪古觯瑑?nèi)心世界和外面世界是可以相通的。

閉包的應(yīng)用的注意事項(xiàng)

閉包绽乔,在JS中絕對(duì)是一個(gè)高貴的存在弧蝇,它讓很多不可能實(shí)現(xiàn)的代碼成為可能,但是物雖好折砸,也要合理使用看疗,不然不但不能達(dá)到我們想要的效果,有的時(shí)候可能還會(huì)適得其反睦授。

內(nèi)存泄漏(Memory Leak)

JavaScript分配給Web瀏覽器的可用內(nèi)存數(shù)量通常比分配給桌面應(yīng)用程序的少两芳,這樣做主要是防止JavaScript的網(wǎng)頁(yè)耗盡全部系統(tǒng)內(nèi)存而導(dǎo)致系統(tǒng)崩潰。

因此去枷,要想使頁(yè)面具有更好的性能怖辆,就必須確保頁(yè)面占用最少的內(nèi)存資源,也就是說(shuō)沉填,我們應(yīng)該保證執(zhí)行代碼只保存有用的數(shù)據(jù)疗隶,一旦數(shù)據(jù)不再有用,我們就應(yīng)該讓垃圾回收機(jī)制對(duì)其進(jìn)行回收翼闹,釋放內(nèi)存。

我們現(xiàn)在都知道了閉包阻止了垃圾回收機(jī)制對(duì)變量進(jìn)行回收蒋纬,因此變量會(huì)永遠(yuǎn)存在內(nèi)存中猎荠,即使當(dāng)變量不再被使用時(shí),這樣會(huì)造成內(nèi)存泄漏蜀备,會(huì)嚴(yán)重影響頁(yè)面的性能关摇。因此當(dāng)變量對(duì)象不再適用時(shí),我們要將其釋放碾阁。

我們拿上面代碼舉例:

function foo() {

 let a = 2;



 function bar() {

     console.log( a );

 }



 return bar;

}

let baz = foo();

baz(); //baz指向的對(duì)象會(huì)永遠(yuǎn)存在堆內(nèi)存中

baz = null; //如果baz不再使用输虱,將其指向的對(duì)象釋放

關(guān)于內(nèi)存泄漏,推薦 阮一峰老師博客脂凶。

閉包的應(yīng)用

1.模塊

一個(gè)模塊應(yīng)該具有私有屬性宪睹、私有方法和公有屬性愁茁、公有方法。

而閉包能很好的將模塊的公有屬性亭病、方法暴露出來(lái)鹅很。

var myModule = (function (window, undefined) {

let name = "echo";



function getName() {

    return name;

}



return {

    name,

    getName

}

})(window);

console.log( myModule.name ); // echo

console.log( myModule.getName() ); // echo

"return"關(guān)鍵字將對(duì)象引用導(dǎo)出賦值給myModule,從而應(yīng)用到閉包罪帖。

2.延時(shí)器(setTimeout)促煮、計(jì)數(shù)器(setInterval)

這里簡(jiǎn)單寫一個(gè)常見的關(guān)于閉包的面試題。

for( var i = 0; i < 5; i++ ) {

setTimeout(() => {

    console.log( i );

}, 1000 * i)

}

答案大家都知道:每秒鐘輸出一個(gè)5整袁,一共輸出5次菠齿。

那么如何做到每秒鐘輸出一個(gè)數(shù),以此為0坐昙,1泞当,2,3民珍,4呢襟士?

我們這里只介紹閉包的解決方法,其他類似塊作用域等等的解決方法嚷量,我們這里不討論陋桂。

for( var i = 0; i < 5; i++ ) {

((j) => {

    setTimeout(() => {

        console.log( j );

    }, 1000 * j)

})(i)   

}

"setTimeout"方法里應(yīng)用了閉包,使其內(nèi)部能夠記住每次循環(huán)所在的詞法作用域和作用域鏈蝶溶。

由于setTimeout中的回調(diào)函數(shù)會(huì)在當(dāng)前任務(wù)隊(duì)列的尾部進(jìn)行執(zhí)行嗜历,因此上面第一個(gè)例子中每次循環(huán)中的setTimeout回調(diào)函數(shù)記住的i的值是for循環(huán)作用域中的值,此時(shí)都是5抖所,而第二個(gè)例子記住的i的數(shù)為setTimeout的父級(jí)作用域自執(zhí)行函數(shù)中的j的值梨州,依次為0,1田轧,2暴匠,3,4傻粘。

3.監(jiān)聽器

var oDiv = document.querySeletor("#div");

oDiv.addEventListener('click', function() {

console.log( oDiv.id );

})

=- 關(guān)于閉包每窖,我覺得我說(shuō)清楚了,你看清楚了嗎弦悉?留言告訴我吧 -=

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末窒典,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子稽莉,更是在濱河造成了極大的恐慌瀑志,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異劈猪,居然都是意外死亡昧甘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門岸霹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)疾层,“玉大人,你說(shuō)我怎么就攤上這事贡避⊥蠢瑁” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵刮吧,是天一觀的道長(zhǎng)湖饱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)杀捻,這世上最難降的妖魔是什么井厌? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮致讥,結(jié)果婚禮上仅仆,老公的妹妹穿的比我還像新娘。我一直安慰自己垢袱,他們只是感情好墓拜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著请契,像睡著了一般咳榜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上爽锥,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天涌韩,我揣著相機(jī)與錄音,去河邊找鬼氯夷。 笑死臣樱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肠槽。 我是一名探鬼主播擎淤,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼秸仙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起桩盲,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤寂纪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捞蛋,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡孝冒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拟杉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庄涡。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖搬设,靈堂內(nèi)的尸體忽然破棺而出穴店,到底是詐尸還是另有隱情,我是刑警寧澤拿穴,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布泣洞,位于F島的核電站,受9級(jí)特大地震影響默色,放射性物質(zhì)發(fā)生泄漏球凰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一腿宰、第九天 我趴在偏房一處隱蔽的房頂上張望呕诉。 院中可真熱鬧,春花似錦吃度、人聲如沸甩挫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捶闸。三九已至,卻和暖如春拖刃,著一層夾襖步出監(jiān)牢的瞬間删壮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工兑牡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留央碟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓均函,卻偏偏與公主長(zhǎng)得像亿虽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苞也,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容