亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

js閉包的本質(zhì)

qianfeng / 3346人閱讀

摘要:也正因?yàn)檫@個(gè)閉包的特性,閉包函數(shù)可以讓父函數(shù)的數(shù)據(jù)一直駐留在內(nèi)存中保存,從而這也是后來模塊化的基礎(chǔ)。只有閉包函數(shù),可以讓它的父函數(shù)作用域永恒,像全局作用域,一直在內(nèi)存中存在。的本質(zhì)就是如此,每個(gè)模塊文件就是一個(gè)大閉包。

為什么會(huì)有閉包

js之所以會(huì)有閉包,是因?yàn)閖s不同于其他規(guī)范的語言,js允許一個(gè)函數(shù)中再嵌套子函數(shù),正是因?yàn)檫@種允許函數(shù)嵌套,導(dǎo)致js出現(xiàn)了所謂閉包。

function a(){
    function b(){
    
    };
    b();
}
a();

在js正常的函數(shù)嵌套中,父函數(shù)a調(diào)用時(shí),嵌套的子函數(shù)b的結(jié)構(gòu),在內(nèi)存中產(chǎn)生,然后子函數(shù)又接著調(diào)用了,子函數(shù)b就注銷了,此時(shí)父函數(shù)a也就執(zhí)行到尾,父函數(shù)a也會(huì)把自己函數(shù)體內(nèi)調(diào)用時(shí)生成的數(shù)據(jù)從內(nèi)存都注銷。

function a(){
    function b(){
    
    }
    return b;
}
var f=a();

這個(gè)例子中,父函數(shù)調(diào)用時(shí),函數(shù)體內(nèi)創(chuàng)建了子函數(shù)b,但是子函數(shù)并沒有立即調(diào)用,而是返回了函數(shù)指針,以備“日后再調(diào)用”,因?yàn)椤皽?zhǔn)備日后調(diào)用”,此時(shí)父函數(shù)a執(zhí)行完了,就不敢注銷自己的作用域中的數(shù)據(jù)了,因?yàn)橐坏┳N了,子函數(shù)b日后再調(diào)用時(shí),沿著函數(shù)作用域鏈往上訪問數(shù)據(jù),就沒有數(shù)據(jù)可以訪問了,這就違背了js函數(shù)作用域鏈的機(jī)制。

正因此,子函數(shù)要“日后調(diào)用”,導(dǎo)致父函數(shù)要維持函數(shù)作用域鏈,而不敢注銷自己的作用域,那么這個(gè)子函數(shù)就是“閉包函數(shù)”。

閉包函數(shù)在形式上有很多種。

在這個(gè)例子中,父函數(shù)v()體內(nèi)定義了好幾種子函數(shù),這些子函數(shù)有的是異步事件的回調(diào)函數(shù),會(huì)進(jìn)入瀏覽器的事件循環(huán)池,等主線程工作結(jié)束后日后再調(diào)用這些回調(diào)函數(shù),這些子函數(shù),都導(dǎo)致父函數(shù)調(diào)用完了,不敢注銷自己的作用域,因此這些子函數(shù)都是閉包函數(shù)。

js并不是為了創(chuàng)造閉包而創(chuàng)造,完全只是因?yàn)閖s允許函數(shù)嵌套,js函數(shù)嵌套還有個(gè)函數(shù)作用域鏈的機(jī)制,讓父函數(shù)不敢注銷自己作用域中的數(shù)據(jù),才會(huì)產(chǎn)生所謂閉包。

也正因?yàn)檫@個(gè)閉包的特性,閉包函數(shù)可以讓父函數(shù)的數(shù)據(jù)一直駐留在內(nèi)存中保存,從而這也是后來js模塊化的基礎(chǔ)。

閉包與函數(shù)作用域

如果僅僅只是有函數(shù)嵌套,而沒有函數(shù)作用域鏈,也或許不會(huì)有閉包。理解js函數(shù)作用域至關(guān)重要。

function a(){

}

函數(shù)的作用域?qū)嶋H上是個(gè)動(dòng)態(tài)概念,上面的代碼,只是定義了一個(gè)函數(shù),并沒有調(diào)用函數(shù),函數(shù)的作用域是不存在的。只有函數(shù)a調(diào)用時(shí),才會(huì)在內(nèi)存中動(dòng)態(tài)開辟一個(gè)自己的作用域,函數(shù)調(diào)用完了這個(gè)作用域又關(guān)閉了,函數(shù)運(yùn)行過程中在內(nèi)存創(chuàng)建的數(shù)據(jù)又被清除了。

function a(){
    var n=1;
    function b(){
        n++;
        console.log(n);
    }
    b();
    b();
    b();
}
a();

這個(gè)例子中,父函數(shù)a調(diào)用,首先在內(nèi)存中動(dòng)態(tài)開辟了作用域,然后在運(yùn)算過程中,定義了函數(shù)b,子函數(shù)b()每次調(diào)用,都會(huì)開辟自己的作用域,在自己的作用域內(nèi)進(jìn)行運(yùn)算,運(yùn)算過程中訪問了還處于打開狀態(tài)的父函數(shù)作用域中的變量n的值,這個(gè)子函數(shù)三次調(diào)用,每次調(diào)用時(shí)候自己子作用域,訪問的都是同一個(gè)變量n。但是父函數(shù)a總有執(zhí)行完的時(shí)刻,總有要關(guān)閉作用域的時(shí)候。

var q="";
function a(){
    var n=1;
    q=function b(){
        n++;
        console.log(n);
    }
}
a();
q();
q();
q();

這個(gè)例子中,運(yùn)行父函數(shù),函數(shù)開啟了作用域,運(yùn)算過程中生成了函數(shù)b,子函數(shù)b賦給了全局變量q,導(dǎo)致父函數(shù)a運(yùn)行完了,不敢關(guān)閉自己的作用域,,讓子函數(shù)b成了閉包函數(shù),全局變量q持有了這個(gè)閉包函數(shù)。

這個(gè)得到的結(jié)果,和上面例子中常規(guī)函數(shù)嵌套,得到的效果是一樣的。但是區(qū)別在于,前一個(gè)例子中,父函數(shù)a即便執(zhí)行萬年,也有結(jié)束要關(guān)閉作用域的時(shí)候,而這個(gè)閉包,就讓它的父函數(shù)作用域永恒了。

實(shí)際上在js的作用域機(jī)制中,有一個(gè)作用域是永恒的,就是window全局作用域,只要瀏覽器窗口不關(guān)閉,這個(gè)windows全局作用域就是永恒的,在全局作用域中定一個(gè)函數(shù),無論調(diào)用幾次,這幾次調(diào)用都可以共享操作同一個(gè)全局變量。除了window作用域可以永恒,其他的函數(shù)作用域,總有關(guān)閉的時(shí)候而無法永恒。只有閉包函數(shù),可以讓它的父函數(shù)作用域永恒,像windows全局作用域,一直在內(nèi)存中存在。

當(dāng)閉包函數(shù)調(diào)用時(shí),它會(huì)動(dòng)態(tài)開辟出自己的作用域,在它之上的是父函數(shù)的永恒作用域,在父函數(shù)作用域之上的,是window永恒的全局作用域。閉包函數(shù)調(diào)用完了,它自己的作用域關(guān)閉了,從內(nèi)存中消失了,但是父函數(shù)的永恒作用域和window永恒作用域還一直在內(nèi)存是打開的。閉包函數(shù)再次調(diào)用時(shí),還能訪問這兩個(gè)作用域,可能還保存了它上次調(diào)用時(shí)候產(chǎn)生的數(shù)據(jù)。只有當(dāng)閉包函數(shù)的引用被釋放了,它的父作用域才會(huì)最終關(guān)閉(當(dāng)然父函數(shù)可能創(chuàng)建了多個(gè)閉包函數(shù),就需要多個(gè)閉包函數(shù)全部釋放后,父函數(shù)作用域才會(huì)關(guān)閉)。

這個(gè)例子是閉包函數(shù)的一個(gè)典型應(yīng)用,示例中只有兩個(gè)函數(shù)嵌套,但是加上window全局作用于,一共會(huì)有三個(gè)嵌套作用域。其中for循環(huán)了三次,三次調(diào)用了匿名自執(zhí)行函數(shù),就開了三個(gè)函數(shù)作用域,開第一個(gè)作用域時(shí)保存的i的值是0,開第二個(gè)作用域保存的是1,第三個(gè)保存的是2。三次調(diào)用父函數(shù),又創(chuàng)建了三個(gè)閉包函數(shù),每個(gè)閉包函數(shù)沿著它自己的作用域鏈向上訪問,訪問的值就都不相同。三個(gè)閉包函數(shù)調(diào)用完了,它們自己的作用域就關(guān)閉了,但是各自的父函數(shù)作用域還一直在內(nèi)存中處于打開狀態(tài),下次閉包函數(shù)再調(diào)用時(shí),再接著訪問它自己的作用域。就像在window全局作用域定義了一個(gè)函數(shù),函數(shù)調(diào)用幾次,全局作用域都在,每次調(diào)用都接著訪問全局作用域。

閉包與js模塊化

日常編碼中有很多地方會(huì)不經(jīng)意用到了閉包只是沒有察覺,使用閉包的作用就是為了兩點(diǎn):形成命名空間同時(shí)保存數(shù)據(jù)。

在HTML中引入多個(gè)js文件,瀏覽器會(huì)從第一個(gè)執(zhí)行到最后一個(gè),這些js文件都共用一個(gè)全局作用域,這很多時(shí)候就會(huì)導(dǎo)致命名沖突。而如果只是為了命名空間,匿名自執(zhí)行函數(shù)也可以實(shí)現(xiàn)。

(function(){
    var a=1;
})()
alert(a);//訪問不到變量a的值,會(huì)報(bào)錯(cuò)變量a未定義

這個(gè)例子中就借助匿名自執(zhí)行函數(shù)實(shí)現(xiàn)了命名空間,隔離了數(shù)據(jù),不會(huì)產(chǎn)生沖突,但是僅僅只是把數(shù)據(jù)封起來不提供接口有些時(shí)候或許也不行,因此這就需要閉包。

這個(gè)例子在前一個(gè)例子基礎(chǔ)上進(jìn)行了改造,a.js文件中就使用了閉包,無論這個(gè)文件引入到哪里,它的數(shù)據(jù)都是隔離的,不會(huì)會(huì)任何地方的代碼產(chǎn)生沖突,同時(shí)它提供了閉包函數(shù)作為API接口,讓其他地方以指定的方式訪問數(shù)據(jù),得到需要的結(jié)果,其他地方也不需要關(guān)心閉包結(jié)構(gòu)里的數(shù)據(jù)是什么或者怎么操作的,也不需要擔(dān)心引入它會(huì)與自己的代碼沖突。

require.js的本質(zhì)就是如此,每個(gè)模塊文件就是一個(gè)大閉包。

是否使用閉包要考慮兩點(diǎn):隔離和數(shù)據(jù)保存。如果需要隔離數(shù)據(jù)形成命名空間,可以使用匿名自執(zhí)行函數(shù)。如果需要隔離數(shù)據(jù),同時(shí)還需要在隔離狀態(tài)保存數(shù)據(jù),保存了后面還可以繼續(xù)使用,那就可以使用閉包。如果都不需要,那就使用普通函數(shù),函數(shù)調(diào)用完作用域就關(guān)閉數(shù)據(jù)就釋放了,沒有保存,數(shù)據(jù)不存在了也不需要隔離了。

一個(gè)實(shí)際應(yīng)用

淘寶的購物車中,一個(gè)商品點(diǎn)擊新增數(shù)量或減少數(shù)量,它會(huì)往服務(wù)器發(fā)送一個(gè)請(qǐng)求保存新數(shù)量,但是如果快速連續(xù)點(diǎn)擊,淘寶的購物車并沒有跟隨快速點(diǎn)擊連續(xù)發(fā)送ajax,而是在連續(xù)點(diǎn)擊的結(jié)束之后才發(fā)送了一個(gè)請(qǐng)求,把用戶真正想要的數(shù)量最后才用一個(gè)請(qǐng)求發(fā)送了服務(wù)器,這樣就減少了不必要的請(qǐng)求減少服務(wù)器的壓力。

如果只是單純用個(gè)click事件處理函數(shù),然后把a(bǔ)jax放到處理函數(shù)中,點(diǎn)一次按鈕就會(huì)發(fā)一次請(qǐng)求,連續(xù)點(diǎn)就會(huì)連續(xù)發(fā)。而要實(shí)現(xiàn)淘寶的這個(gè)效果,它要的原理是,定一個(gè)延時(shí)時(shí)間,比方1秒,單擊之后過1秒種才發(fā)請(qǐng)求,而如果單擊了之后還沒有到1秒又連續(xù)單擊了,那么重置這個(gè)計(jì)時(shí),快速連續(xù)單擊就一直再重置這個(gè)計(jì)時(shí)始終都沒有達(dá)到一秒,就不會(huì)因?yàn)檫B續(xù)點(diǎn)擊而發(fā)送請(qǐng)求,直到最后連續(xù)點(diǎn)擊停下來了,過了一秒才發(fā)一個(gè)請(qǐng)求。

這個(gè)應(yīng)用中就借助了閉包函數(shù),實(shí)際click事件真正執(zhí)行的用于發(fā)送請(qǐng)求的也就是里面嵌套的紅框的閉包函數(shù),每一次單擊都會(huì)執(zhí)行這個(gè)紅框函數(shù),它除了最終發(fā)送ajax,還要做個(gè)判斷,如果上一次點(diǎn)擊的時(shí)間,到這一次又點(diǎn)擊的時(shí)間,這之間的間隔小于了指定的1秒,那么就不會(huì)發(fā)送ajax,同時(shí)重置這個(gè)計(jì)時(shí)。而在最初第一次單擊的時(shí)候,它還需要上一次的時(shí)間,這個(gè)時(shí)間就只能在初始化時(shí)候用一個(gè)變量保存一個(gè)當(dāng)前時(shí)間,然后第一次單擊時(shí)候的時(shí)間與變量保存的時(shí)間進(jìn)行一個(gè)對(duì)比。單擊第二次時(shí),那么該變量又保存了第一次單擊時(shí)的時(shí)間,然后第二次單擊的時(shí)間又與第一次單擊的時(shí)間進(jìn)行比較。

關(guān)鍵也就在于需要個(gè)變量保存上一次的時(shí)間。這時(shí)間不借助閉包函數(shù)也完全可以,就把這個(gè)變量放在全局環(huán)境下,在全局環(huán)境下定義一個(gè)全局變量startTime,反正就是保存一下上一次單擊的時(shí)間。但是問題在于,購物車中有多個(gè)商品,并不會(huì)有只有一個(gè)單擊按鈕需要用到這個(gè),多個(gè)按鈕要用,給每個(gè)按鈕都定義全局變量,startOne,startTwo,startThree...那就很麻煩,并且通過json渲染多個(gè)商品時(shí)候也不可能手動(dòng)去定義這么多變量。這就必需借助閉包函數(shù)。

json在渲染多個(gè)商品時(shí)按鈕時(shí),這個(gè)debounce函數(shù)就會(huì)被多次調(diào)用,每一次調(diào)用都return返回了一個(gè)閉包函數(shù)給每個(gè)商品的button按鈕的click作為其處理函數(shù),那么每個(gè)處理函數(shù)都有一個(gè)專屬的永恒父作用域,并且里面都已經(jīng)自動(dòng)定義了各自需要使用的startTime變量用于保存每個(gè)按鈕自己計(jì)算時(shí)使用的上一次單擊的時(shí)間。通過閉包解決這個(gè)問題這就非常方便。

額...

上面一個(gè)通過for()循環(huán)創(chuàng)建多個(gè)閉包函數(shù),內(nèi)存開多個(gè)作用域來保存不同的數(shù)據(jù),不一定是最好的實(shí)現(xiàn)。這個(gè)例子中,同樣是for循環(huán)創(chuàng)建三個(gè)了函數(shù),但三個(gè)函數(shù)都是普通函數(shù)。由于函數(shù)在js中也是對(duì)象,因此給函數(shù)本身創(chuàng)建一個(gè)靜態(tài)屬性來保存不同的值,那么for循環(huán)創(chuàng)建的三個(gè)普通函數(shù),每個(gè)函數(shù)的靜態(tài)屬性都保存了不同的值,而不必借助閉包結(jié)構(gòu)保存不同的值,可以減少內(nèi)存消耗。

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

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

相關(guān)文章

  • 詳解js閉包

    摘要:定義函數(shù)的時(shí)候,為什么的值重新從開始了因?yàn)橛忠淮芜\(yùn)行了函數(shù),生成一個(gè)新的的活動(dòng)對(duì)象,所以的作用域鏈引用的是一個(gè)新的值。 前言 在js中,閉包是一個(gè)很重要又相當(dāng)不容易完全理解的要點(diǎn),網(wǎng)上關(guān)于講解閉包的文章非常多,但是并不是非常容易讀懂,在這里以《javascript高級(jí)程序設(shè)計(jì)》里面的理論為基礎(chǔ)。用拆分的方式,深入講解一下對(duì)于閉包的理解,如果有不對(duì)請(qǐng)指正。 寫在閉包之前 閉包的內(nèi)部細(xì)節(jié),...

    chaosx110 評(píng)論0 收藏0
  • 一次阿里面試后對(duì)函數(shù)本質(zhì)理解

    摘要:函數(shù)使用函數(shù)的使用主要有兩種閉包閉包的本質(zhì)是對(duì)共享變量的操作,典型運(yùn)用是觀察者模式備忘錄模式普通封裝,復(fù)用。參考阿里博客你可能不知道的事基礎(chǔ)篇總結(jié)要寫好一個(gè)項(xiàng)目需要兼容,性能,安全等。 一次阿里面試后對(duì)函數(shù)本質(zhì)的理解 寫在前面 環(huán)境:阿里的在線編程系統(tǒng)允許面試官在線考察面試者的編程能力. 考點(diǎn):編程和理論. 編程:分為技術(shù)自驅(qū)力、異步操作、編程風(fēng)格(顆粒小)、變量作用域、DOM操作...

    liuyix 評(píng)論0 收藏0
  • 一次阿里面試后對(duì)函數(shù)本質(zhì)理解

    摘要:函數(shù)使用函數(shù)的使用主要有兩種閉包閉包的本質(zhì)是對(duì)共享變量的操作,典型運(yùn)用是觀察者模式備忘錄模式普通封裝,復(fù)用。參考阿里博客你可能不知道的事基礎(chǔ)篇總結(jié)要寫好一個(gè)項(xiàng)目需要兼容,性能,安全等。 一次阿里面試后對(duì)函數(shù)本質(zhì)的理解 寫在前面 環(huán)境:阿里的在線編程系統(tǒng)允許面試官在線考察面試者的編程能力. 考點(diǎn):編程和理論. 編程:分為技術(shù)自驅(qū)力、異步操作、編程風(fēng)格(顆粒小)、變量作用域、DOM操作...

    superw 評(píng)論0 收藏0
  • 一次阿里面試后對(duì)函數(shù)本質(zhì)理解

    摘要:函數(shù)使用函數(shù)的使用主要有兩種閉包閉包的本質(zhì)是對(duì)共享變量的操作,典型運(yùn)用是觀察者模式備忘錄模式普通封裝,復(fù)用。參考阿里博客你可能不知道的事基礎(chǔ)篇總結(jié)要寫好一個(gè)項(xiàng)目需要兼容,性能,安全等。 一次阿里面試后對(duì)函數(shù)本質(zhì)的理解 寫在前面 環(huán)境:阿里的在線編程系統(tǒng)允許面試官在線考察面試者的編程能力. 考點(diǎn):編程和理論. 編程:分為技術(shù)自驅(qū)力、異步操作、編程風(fēng)格(顆粒小)、變量作用域、DOM操作...

    jeyhan 評(píng)論0 收藏0
  • 簡(jiǎn)述作用域還有閉包延伸至模塊化

    摘要:首先變量對(duì)于一個(gè)程序來說是一個(gè)很重要的角色那么問題來了這些變量存在哪里程序用到的時(shí)候如何找到變量呢所以需要一套規(guī)則來存儲(chǔ)變量方便之后再找到這套規(guī)則就成為作用域是一門編譯語言對(duì)于來說大部分情況下編譯發(fā)生在代碼執(zhí)行前的幾微妙的時(shí)間內(nèi)對(duì)于參與到一 首先,變量對(duì)于一個(gè)程序來說是一個(gè)很重要的角色, 那么問題來了 這些變量存在哪里,程序用到的時(shí)候如何找到變量呢? 所以需要一套規(guī)則來存儲(chǔ)變量方便之后...

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

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

0條評(píng)論

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