成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

深入理解JavaScript(二):由一道題來(lái)思考閉包

曹金海 / 1300人閱讀

摘要:中所有的事件綁定都是異步編程當(dāng)前這件事件沒(méi)有徹底完成,不再等待,繼續(xù)執(zhí)行下面的任務(wù)當(dāng)綁定事件后,不需要等待執(zhí)行,繼續(xù)執(zhí)行下一個(gè)循環(huán)任務(wù),所以當(dāng)我們點(diǎn)擊執(zhí)行方法的時(shí)候,循環(huán)早已結(jié)束即是最后。

概念
閉包就是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)
    
    
    
    
    點(diǎn)擊li標(biāo)簽彈出對(duì)應(yīng)數(shù)字
    
    
    
  • 0
  • 1
  • 2
  • 3

如上題,最為常見(jiàn)的一個(gè)例子,這里解釋由這道題引出的js知識(shí)點(diǎn),如上我們知道在瀏覽器運(yùn)行無(wú)論點(diǎn)擊哪個(gè)li標(biāo)簽都是彈出3,首先來(lái)理解為什么會(huì)彈出3。

程序通過(guò)for循環(huán)給每個(gè)li標(biāo)簽綁定了事件,然后通過(guò)點(diǎn)擊li標(biāo)簽觸發(fā)方法,即執(zhí)行alert(i)。js中有個(gè)作用域鏈查找機(jī)制,首先會(huì)在onclick返回的函數(shù)作用域查找i變量的值,找不到則往上一層找i,上一層即是window全局作用域,即找到全局變量i,即for循環(huán)定義的i。

注意for循環(huán)的i并不是私有變量,而是全局變量。

js中所有的事件綁定都是異步編程(當(dāng)前這件事件沒(méi)有徹底完成,不再等待,繼續(xù)執(zhí)行下面的任務(wù))

當(dāng)綁定onclick事件后,不需要等待執(zhí)行,繼續(xù)執(zhí)行下一個(gè)循環(huán)任務(wù),所以當(dāng)我們點(diǎn)擊執(zhí)行方法的時(shí)候,循環(huán)早已結(jié)束,即是最后i=3。故程序執(zhí)行后全局變量i被循環(huán)執(zhí)行后賦值為最終的3,所以當(dāng)點(diǎn)擊的時(shí)候,外層循環(huán)已經(jīng)結(jié)束,頁(yè)面加載完成預(yù)示著js代碼都已經(jīng)執(zhí)行完成,即執(zhí)行alert(i)時(shí),由于i不是私有變量,便會(huì)找到上一級(jí)window作用域全局的i,所以無(wú)論點(diǎn)擊哪個(gè)li標(biāo)簽都是彈出3

為什么要用閉包

那么,解決這個(gè)問(wèn)題的緣由,在于i每次在頁(yè)面加載完就賦值為3,alert(i)的時(shí)候總是找到全局變量i。在ES5傳統(tǒng)語(yǔ)法中,能形成作用域的只有全局和函數(shù),現(xiàn)在每次i找的都是全局,那么要保住i的值只能在全局和onclick返回函數(shù)的作用域中間再加一個(gè)小的私有作用域,即是大的作用域外再加一個(gè)小的作用域,這樣i往上一層作用域查找時(shí),就會(huì)獲取小作用域的i的值,而不會(huì)去獲取全局變量的i值。

這個(gè)思路解決問(wèn)題就需要引入閉包,在這個(gè)理解上閉包是指函數(shù)變量可以保存在函數(shù)作用域內(nèi),因此看起來(lái)是函數(shù)將變量“包裹”了起來(lái)。于是,代碼改成:

for(var i = 0;i < list.length;i++){
    list[i].onclick = (function(n){//形參n
        //=>讓自執(zhí)行函數(shù)執(zhí)行,把執(zhí)行的返回值(return)賦值給onclick
        //(此處onclick綁定的是返回的小函數(shù),點(diǎn)擊的時(shí)候執(zhí)行的是小函數(shù)),
        // 自執(zhí)行函數(shù)在給事件賦值的時(shí)候就已經(jīng)執(zhí)行了
        // 自執(zhí)行函數(shù)形成一個(gè)私有作用域
        var i = n;
        return function(){
            alert(i);
        }
    })(i);//傳入實(shí)參i
}

循環(huán)三次,形成三個(gè)不銷毀的私有作用域(自執(zhí)行函數(shù)執(zhí)行),而每一個(gè)不銷毀的棧內(nèi)存中都存儲(chǔ)了一個(gè)私有變量i,而這個(gè)值分別是每一次執(zhí)行傳遞進(jìn)來(lái)的全局i的值(也就是:第一個(gè)不銷毀的作用域存儲(chǔ)的是0,第二個(gè)是1,第三個(gè)是2,第四個(gè)是3);當(dāng)點(diǎn)擊的時(shí)候,執(zhí)行返回的小函數(shù),遇到變量i,向它自己的上級(jí)作用域查找。這樣就達(dá)到了我們需要的效果,這種閉包實(shí)現(xiàn),也可以有另一種寫法。

/*原理同法二都是形成三個(gè)不銷毀的私有作用域,分別存儲(chǔ)需要的索引值*/
for(var i = 0;i < list.length;i++){
    (function(n){
        list[n].onclick = function(){
            alert(n);
        } 
    })(i)
}

對(duì)于初始的代碼,如果說(shuō)為什么不能實(shí)現(xiàn),那原因就可歸納為:

1.執(zhí)行方法,形成一個(gè)私有的棧內(nèi)存,遇到變量i,i不是私有變量,向上一級(jí)作用域查找(上級(jí)作用域window)
2.所有的事件綁定都是異步編程,綁定事件后,不需要等待執(zhí)行,繼續(xù)執(zhí)行下一個(gè)循環(huán)任務(wù),所以當(dāng)我們點(diǎn)擊執(zhí)行方法的時(shí)候,循環(huán)早已結(jié)束(讓全局的i等于循環(huán)最后的結(jié)果3)
ES6語(yǔ)法的解決方式

在ES6中,解決這種問(wèn)題只需要一個(gè)let變量,ES6中才有塊級(jí)作用域(類似于私有作用域)的概念

for(let i = 0;i < list.length;i++){
    list[i].onclick = function(){
        alert(i);
    }
}
閉包對(duì)內(nèi)存的影響

從上面可知,每次for都會(huì)形成一個(gè)私有作用域,每個(gè)都里面保存的變量i的值,程序運(yùn)行后這些作用域并不會(huì)被銷毀,所以由于閉包會(huì)攜帶包含它的函數(shù)的作用域,所以會(huì)比其他函數(shù)占用更多內(nèi)容,過(guò)度使用閉包會(huì)導(dǎo)致內(nèi)存占用過(guò)多。

在真實(shí)項(xiàng)目中為了保證JS的性能(堆棧內(nèi)存的性能優(yōu)化),應(yīng)該盡可能的減少閉包的使用(不銷毀的堆棧內(nèi)存是耗性能的)

堆內(nèi)存和棧內(nèi)存的釋放

這里又要提到一個(gè)知識(shí)點(diǎn),js中存儲(chǔ)方式的分類:

JS中的內(nèi)存分為堆內(nèi)存和棧內(nèi)存
堆內(nèi)存:存儲(chǔ)引用數(shù)據(jù)類型值(對(duì)象:鍵值對(duì) 函數(shù):代碼字符串)
棧內(nèi)存:提供JS代碼執(zhí)行的環(huán)境和存儲(chǔ)基本類型值

粗暴理解var定義的變量存在棧內(nèi)存中,如for循環(huán)中的i是存在棧內(nèi)存中的;而函數(shù),它是存在堆內(nèi)存中

一般情況下,當(dāng)函數(shù)執(zhí)行完成,所形成的私有作用域(棧內(nèi)存)都會(huì)自動(dòng)釋放掉(在棧內(nèi)存中存儲(chǔ)的值也都會(huì)釋放掉),那為什么閉包的棧內(nèi)存不會(huì)被自動(dòng)釋放掉,在js中也有特殊不被銷毀的情況:

1.函數(shù)執(zhí)行完成,當(dāng)前形成的棧內(nèi)存中,某些內(nèi)容被棧內(nèi)存以外的變量占用了,此時(shí)棧內(nèi)存不能釋放(一旦釋放外面找不到原有的內(nèi)容了)
2.全局棧內(nèi)存只有在頁(yè)面關(guān)閉的時(shí)候才會(huì)被釋放掉

閉包則是屬于第一種情況,onclick函數(shù)形成的棧內(nèi)存,被小函數(shù)【alert(i),i找到onclick作用域獲取i值】占用了onclick函數(shù)的棧內(nèi)存(變量i是存在棧內(nèi)存中),故棧內(nèi)存不能被釋放,所以才會(huì)說(shuō)閉包過(guò)度使用容易導(dǎo)致內(nèi)存被占用過(guò)多,因?yàn)椴粫?huì)自動(dòng)釋放內(nèi)存。

堆內(nèi)存的釋放

堆內(nèi)存讓所有引用堆內(nèi)存空間地址的變量賦值為null即可(沒(méi)有變量占用這個(gè)堆內(nèi)存了,瀏覽器會(huì)在空閑的時(shí)候把它釋放掉)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/106351.html

相關(guān)文章

  • 【進(jìn)階1-4期】JavaScript深入之帶你走進(jìn)內(nèi)存機(jī)制

    摘要:引擎對(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ì)劃...

    不知名網(wǎng)友 評(píng)論0 收藏0
  • JavaScript深入之執(zhí)行上下文

    摘要:深入系列第七篇,結(jié)合之前所講的四篇文章,以權(quán)威指南的為例,具體講解當(dāng)函數(shù)執(zhí)行的時(shí)候,執(zhí)行上下文棧變量對(duì)象作用域鏈?zhǔn)侨绾巫兓?。前言在深入之?zhí)行上下文棧中講到,當(dāng)代碼執(zhí)行一段可執(zhí)行代碼時(shí),會(huì)創(chuàng)建對(duì)應(yīng)的執(zhí)行上下文。 JavaScript深入系列第七篇,結(jié)合之前所講的四篇文章,以權(quán)威指南的demo為例,具體講解當(dāng)函數(shù)執(zhí)行的時(shí)候,執(zhí)行上下文棧、變量對(duì)象、作用域鏈?zhǔn)侨绾巫兓摹?前言 在《Jav...

    gougoujiang 評(píng)論0 收藏0
  • web前端面試題一

    摘要:需求一個(gè)輸入框,用戶輸入時(shí)有聯(lián)想搜索,每次用戶輸入都會(huì)觸發(fā)請(qǐng)求,過(guò)多的請(qǐng)求會(huì)造成服務(wù)器的壓力,如何去解決這個(gè)問(wèn)題請(qǐng)求函數(shù)面試者延遲發(fā)送可以去解決這樣的問(wèn)題。 寫在前面的話 一般來(lái)說(shuō),面試質(zhì)量的高低很大程度影響公司是否想接受改人才,也影響了人才是否愿意去公司。質(zhì)量高的面試,公司能表明對(duì)人才的要求,個(gè)人也能表明所期待的公司是一個(gè)什么模式的公司。最終會(huì)有利于雙向選擇的過(guò)程。能盡早的把問(wèn)題暴露...

    bergwhite 評(píng)論0 收藏0
  • 程序語(yǔ)言

    摘要:一面應(yīng)該還問(wèn)了其他內(nèi)容,但是兩次面試多線程面試問(wèn)題和答案采訪中,我們通常會(huì)遇到兩個(gè)主題采集問(wèn)題和多線程面試問(wèn)題。多線程是關(guān)于并發(fā)和線程的。我們正在共享重要的多線程面試問(wèn)題和答案。。 2016 年末,騰訊,百度,華為,搜狗和滴滴面試題匯總 2016 年未,騰訊,百度,華為,搜狗和滴滴面試題匯總 【碼農(nóng)每日一題】Java 內(nèi)部類(Part 2)相關(guān)面試題 關(guān)注一下嘛,又不讓你背鍋!問(wèn):Ja...

    mtunique 評(píng)論0 收藏0
  • 程序語(yǔ)言

    摘要:一面應(yīng)該還問(wèn)了其他內(nèi)容,但是兩次面試多線程面試問(wèn)題和答案采訪中,我們通常會(huì)遇到兩個(gè)主題采集問(wèn)題和多線程面試問(wèn)題。多線程是關(guān)于并發(fā)和線程的。我們正在共享重要的多線程面試問(wèn)題和答案。。 2016 年末,騰訊,百度,華為,搜狗和滴滴面試題匯總 2016 年未,騰訊,百度,華為,搜狗和滴滴面試題匯總 【碼農(nóng)每日一題】Java 內(nèi)部類(Part 2)相關(guān)面試題 關(guān)注一下嘛,又不讓你背鍋!問(wèn):Ja...

    stefan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<