国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

js閉包的本質

qianfeng / 3301人閱讀

摘要:也正因為這個閉包的特性,閉包函數可以讓父函數的數據一直駐留在內存中保存,從而這也是后來模塊化的基礎。只有閉包函數,可以讓它的父函數作用域永恒,像全局作用域,一直在內存中存在。的本質就是如此,每個模塊文件就是一個大閉包。

為什么會有閉包

js之所以會有閉包,是因為js不同于其他規范的語言,js允許一個函數中再嵌套子函數,正是因為這種允許函數嵌套,導致js出現了所謂閉包。

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

在js正常的函數嵌套中,父函數a調用時,嵌套的子函數b的結構,在內存中產生,然后子函數又接著調用了,子函數b就注銷了,此時父函數a也就執行到尾,父函數a也會把自己函數體內調用時生成的數據從內存都注銷。

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

這個例子中,父函數調用時,函數體內創建了子函數b,但是子函數并沒有立即調用,而是返回了函數指針,以備“日后再調用”,因為“準備日后調用”,此時父函數a執行完了,就不敢注銷自己的作用域中的數據了,因為一旦注銷了,子函數b日后再調用時,沿著函數作用域鏈往上訪問數據,就沒有數據可以訪問了,這就違背了js函數作用域鏈的機制。

正因此,子函數要“日后調用”,導致父函數要維持函數作用域鏈,而不敢注銷自己的作用域,那么這個子函數就是“閉包函數”。

閉包函數在形式上有很多種。

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

js并不是為了創造閉包而創造,完全只是因為js允許函數嵌套,js函數嵌套還有個函數作用域鏈的機制,讓父函數不敢注銷自己作用域中的數據,才會產生所謂閉包。

也正因為這個閉包的特性,閉包函數可以讓父函數的數據一直駐留在內存中保存,從而這也是后來js模塊化的基礎。

閉包與函數作用域

如果僅僅只是有函數嵌套,而沒有函數作用域鏈,也或許不會有閉包。理解js函數作用域至關重要。

function a(){

}

函數的作用域實際上是個動態概念,上面的代碼,只是定義了一個函數,并沒有調用函數,函數的作用域是不存在的。只有函數a調用時,才會在內存中動態開辟一個自己的作用域,函數調用完了這個作用域又關閉了,函數運行過程中在內存創建的數據又被清除了。

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

這個例子中,父函數a調用,首先在內存中動態開辟了作用域,然后在運算過程中,定義了函數b,子函數b()每次調用,都會開辟自己的作用域,在自己的作用域內進行運算,運算過程中訪問了還處于打開狀態的父函數作用域中的變量n的值,這個子函數三次調用,每次調用時候自己子作用域,訪問的都是同一個變量n。但是父函數a總有執行完的時刻,總有要關閉作用域的時候。

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

這個例子中,運行父函數,函數開啟了作用域,運算過程中生成了函數b,子函數b賦給了全局變量q,導致父函數a運行完了,不敢關閉自己的作用域,,讓子函數b成了閉包函數,全局變量q持有了這個閉包函數。

這個得到的結果,和上面例子中常規函數嵌套,得到的效果是一樣的。但是區別在于,前一個例子中,父函數a即便執行萬年,也有結束要關閉作用域的時候,而這個閉包,就讓它的父函數作用域永恒了。

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

當閉包函數調用時,它會動態開辟出自己的作用域,在它之上的是父函數的永恒作用域,在父函數作用域之上的,是window永恒的全局作用域。閉包函數調用完了,它自己的作用域關閉了,從內存中消失了,但是父函數的永恒作用域和window永恒作用域還一直在內存是打開的。閉包函數再次調用時,還能訪問這兩個作用域,可能還保存了它上次調用時候產生的數據。只有當閉包函數的引用被釋放了,它的父作用域才會最終關閉(當然父函數可能創建了多個閉包函數,就需要多個閉包函數全部釋放后,父函數作用域才會關閉)。

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

閉包與js模塊化

日常編碼中有很多地方會不經意用到了閉包只是沒有察覺,使用閉包的作用就是為了兩點:形成命名空間同時保存數據。

在HTML中引入多個js文件,瀏覽器會從第一個執行到最后一個,這些js文件都共用一個全局作用域,這很多時候就會導致命名沖突。而如果只是為了命名空間,匿名自執行函數也可以實現。

(function(){
    var a=1;
})()
alert(a);//訪問不到變量a的值,會報錯變量a未定義

這個例子中就借助匿名自執行函數實現了命名空間,隔離了數據,不會產生沖突,但是僅僅只是把數據封起來不提供接口有些時候或許也不行,因此這就需要閉包。

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

require.js的本質就是如此,每個模塊文件就是一個大閉包。

是否使用閉包要考慮兩點:隔離和數據保存。如果需要隔離數據形成命名空間,可以使用匿名自執行函數。如果需要隔離數據,同時還需要在隔離狀態保存數據,保存了后面還可以繼續使用,那就可以使用閉包。如果都不需要,那就使用普通函數,函數調用完作用域就關閉數據就釋放了,沒有保存,數據不存在了也不需要隔離了。

一個實際應用

淘寶的購物車中,一個商品點擊新增數量或減少數量,它會往服務器發送一個請求保存新數量,但是如果快速連續點擊,淘寶的購物車并沒有跟隨快速點擊連續發送ajax,而是在連續點擊的結束之后才發送了一個請求,把用戶真正想要的數量最后才用一個請求發送了服務器,這樣就減少了不必要的請求減少服務器的壓力。

如果只是單純用個click事件處理函數,然后把ajax放到處理函數中,點一次按鈕就會發一次請求,連續點就會連續發。而要實現淘寶的這個效果,它要的原理是,定一個延時時間,比方1秒,單擊之后過1秒種才發請求,而如果單擊了之后還沒有到1秒又連續單擊了,那么重置這個計時,快速連續單擊就一直再重置這個計時始終都沒有達到一秒,就不會因為連續點擊而發送請求,直到最后連續點擊停下來了,過了一秒才發一個請求。

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

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

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

額...

上面一個通過for()循環創建多個閉包函數,內存開多個作用域來保存不同的數據,不一定是最好的實現。這個例子中,同樣是for循環創建三個了函數,但三個函數都是普通函數。由于函數在js中也是對象,因此給函數本身創建一個靜態屬性來保存不同的值,那么for循環創建的三個普通函數,每個函數的靜態屬性都保存了不同的值,而不必借助閉包結構保存不同的值,可以減少內存消耗。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/88140.html

相關文章

  • 詳解js閉包

    摘要:定義函數的時候,為什么的值重新從開始了因為又一次運行了函數,生成一個新的的活動對象,所以的作用域鏈引用的是一個新的值。 前言 在js中,閉包是一個很重要又相當不容易完全理解的要點,網上關于講解閉包的文章非常多,但是并不是非常容易讀懂,在這里以《javascript高級程序設計》里面的理論為基礎。用拆分的方式,深入講解一下對于閉包的理解,如果有不對請指正。 寫在閉包之前 閉包的內部細節,...

    chaosx110 評論0 收藏0
  • 一次阿里面試后對函數本質理解

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

    liuyix 評論0 收藏0
  • 一次阿里面試后對函數本質理解

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

    superw 評論0 收藏0
  • 一次阿里面試后對函數本質理解

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

    jeyhan 評論0 收藏0
  • 簡述作用域還有閉包延伸至模塊化

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

    imingyu 評論0 收藏0

發表評論

0條評論

qianfeng

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<