閉包的理解 防抖節流

2022-06-15 21:15:21 字數 3582 閱讀 4261

閉包是通過改變js**機制保留某作用域的一種手段。當乙個函式執行完畢後,裡面的區域性變數是會被js自帶的垃圾**機制給銷毀的,從而釋放記憶體。但是如果返回乙個函式,而且函式裡面有用到父級函式宣告的變數,那麼此時,變數不會被**,因為還有可能被用到,並且外界可以通過函式訪問這段作用域下的變數。

閉包: 函式執行後返回結果是乙個內部函式,並被外部變數所引用,如果內部函式持有被執行函式作用域的變數,即形成了閉包。

可以在內部函式訪問到外部函式作用域。使用閉包,一可以讀取函式中的變數,二可以將函式中的變數儲存在記憶體中,保護變數不被汙染。而正因閉包會把函式中的變數值儲存在記憶體中,會對記憶體有消耗,所以不能濫用閉包,否則會影響網頁效能,造成記憶體洩漏。當不需要使用閉包時,要及時釋放記憶體,可將內層函式物件的變數賦值為null。

函式執行分成兩個階段(預編譯階段和執行階段)。

利用了函式作用域鏈的特性,乙個函式內部定義的函式會將包含外部函式的活動物件新增到它的作用域鏈中,函式執行完畢,其執行作用域鏈銷毀,但因內部函式的作用域鏈仍然在引用這個活動物件,所以其活動物件不會被銷毀,直到內部函式被燒毀後才被銷毀。

function

foo(c)

}var b = foo(5);//

b = a

b();//6

b();//7

上面的**中,呼叫了兩次函式b,我們執行的是函式b(從foo返回出來的),並沒有重新執行foo,所以也就不會每次給num重新賦值5。至於為什麼會變成這種累加的情況呢,這是因為函式foo執行完後,其內部的的a函式裡面對num有引用,所以foo的作用域以及變數a被保留在了函式a中,返回給了b。

閉包的案例

案例1給4個li註冊點選事件,輸出點選的時候的li的索引,因為點選事件是在js執行完之後才產生的,因此如果沒有設定自定義屬性來記錄li的話,此時列印出來的應該是每次都是 4

var heroes = document.getelementbyid('heroes');

var list =heroes.children;

for (var i = 0; i < list.length; i++)

}

在這裡我們可以用到閉包,把點選事件放到乙個自執行函式中,傳入自執行函式的引數為迴圈變數,這個時候就會發生閉包,由函式的執行環境可以知道,因為點選事件還咩有發生,所以傳進來的引數i會保留存活在記憶體中,所以可以實現輸出對應的索引值。

var heroes = document.getelementbyid('heroes');

var list =heroes.children;

for (var i = 0; i < list.length; i++)

})(i);

}

案例2  定時器的執行過程

js**在執行過程中會有執行棧和任務佇列,當遇到settimout的時候,settimout是非同步任務,會先settimout裡面的function放到任務佇列裡面,當執行棧上的**執行之後,再去執行任務佇列上的function;

console.log('start');

settimeout(

function

() , 0);

console.log('over');

//列印結果為:start over timeout

如果把定時器放在乙個迴圈中,那麼當遇到定時器的時候,會把定時器的函式放到任務佇列之中,當迴圈結束之後,才會執行任務佇列上的定時器函式,因此當i=2是,i++,此時i=3不再滿足跳出迴圈,此時在任務佇列上會儲存有三個0,1,2定時器函式,當執行定時器函式的時候,i=3,此時會列印三個3,

for (var i = 0; i < 3; i++) , 0);

// 3 3 3

因為:由於變數提公升和非同步任務,

可以想象成為這個樣子

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

console.log('a');

settimeout(

function

() );

settimeout(

function

() );

settimeout(

function

() );

settimeout(

function

() );

settimeout(

function

() );

那麼對於上面的定時器函式在乙個迴圈變數之中,如果我們想要每次記錄下來迴圈變數的值,該怎麼辦呢?

因為,let存在塊級作用域,for迴圈和定時器,共享同乙個作用域。

由let宣告的變數,每一次迴圈都會重新宣告變數i,隨後每乙個迴圈都會使用上乙個迴圈結束的值來初始化這個變數i

方法二、利用閉包

可以利用閉包,我們把定時器函式包括成乙個自執行函式,並且他的引數為迴圈變數i這樣每次迴圈的時候,就會在settimeout函式之中儲存乙個i,當我們在執行任務佇列上的定時器函式的時候,就會列印所有的變數i

console.log('start');

for (var i = 0; i < 3; i++) , 0);

})(i);

}console.log('end');

// sart end 0 1 2 

總結閉包的作用

作用1. 使用函式內部的變數在函式執行完後, 仍然存活在記憶體中(延長了區域性變數的生命週期)

作用2. 讓函式外部可以操作(讀寫)到函式內部的資料(變數/函式)       你可以在函式內部使用區域性變數,而不會意外覆蓋同名全域性變數,但仍然能夠訪問到全域性變數。

作用3. 造成記憶體洩漏

閉包的舉例

防抖/節流。

防抖觸發高頻事件後 n 秒內函式只會執行一次,如果 n 秒內高頻事件再次被觸發,則重新計算時間;(不希望每次都觸發,點選按鈕請求介面)

思路:每次觸發事件時都取消之前的延時呼叫方法:

function

debounce(fn) , 500);

};}function

sayhi()

var inp = document.getelementbyid('inp');

inp.addeventlistener('input', debounce(sayhi)); //

防抖

高頻事件觸發,但在 n 秒內只會執行一次,所以節流會稀釋函式的執行頻率。(滾動)

思路:每次觸發事件時都判斷當前是否有等待執行的延時函式。如果有等待執行的函式,則直接返回。

function

throttle(fn) , 500);

};}function

sayhi(e)

window.addeventlistener('resize', throttle(sayhi));

節流的**可以優化為下面

const throttle = (fn,timeout = 1000,_flag=true) => (...args)=>(

_flag && settimeout(()=>,timeout

) && (_flag = false)

防抖與節流方案 函式防抖和節流

在前端開發的過程中,我們經常會需要繫結一些持續觸發的事件,如 resize scroll mousemove 等等,但有些時候我們並不希望在事件持續觸發的過程中那麼頻繁地去執行函式。通常這種情況下我們怎麼去解決的呢?一般來講,防抖和節流是比較好的解決方案。讓我們先來看看在事件持續觸發的過程中頻繁執行...

防抖和節流 什麼是防抖和節流

目錄二 節流 有這樣一種情況,想象有乙個表單,點選提交按鈕就傳送請求給伺服器。如果使用者在很短的時間間隔內 手抖 點選了多次,又或者是惡意點選,那麼就將傳送多個請求。該行為將造成伺服器額外的不必要負載。所謂防抖,實際上就是是處理這種常見的情況的描述。submit該段 當點選submit按鈕的時候,將...

節流與防抖

瀏覽器中某些計算和處理要比其他的昂貴,例如在瀏覽器中操作dom比非dom互動需要更多的記憶體和cpu的事件,連續嘗試進行過多的dom相關操作可能ui導致瀏覽器掛起,有時甚至會崩潰。尤其在ie中使用onresize事件處理程式的時候容易發生,當調整瀏覽器大小的時候,該事件會連續觸發。在onresize...