什麼是函式節流和函式防抖
函式節流和函式防抖是一種優化方法,可用於減少高頻繁觸發任務(函式)的執行次數,達到減少資源佔用的目的。
- 函式節流:任務在指定的間隔時間內只執行一次。
- 函式防抖:只有在任務觸發的間隔大於等於指定的間隔時間,任務才會被執行。
- 區別:在指定時間內,任務執行的次數不同。假設持續觸發一個任務1s,且任務間的觸發間隔為5ms。正常情況下,該任務觸發的次數為1s / 5ms = 200次。使用函式節流後(假設指定間隔時間為50ms),50ms內任務只執行一次,那麼該任務觸發的次數為1s / 50ms = 20次。使用函式防抖後(假設指定間隔為50ms),任務觸發間隔5ms < 指定間隔50ms,因此在這1s內,該任務執行次數為0次。任務在1s時停止觸發後,在1.05s時任務才執行1次。下面我們結合示例來看看函式節流和函式防抖的實現。
我們以滾動條的監聽事件為例,首先看看使用函式節流和函式防抖前的現象。
let count = 0;
$(document).scroll(function () {
console.log(`觸發了${++count}次!`);
});
複製程式碼
可以看到隨著滾動條的拖動,滾動條拖動的回撥函式被頻繁觸發,在回撥比較複雜的時候,頻繁觸發的回撥函式甚至會讓網頁出現掉幀的情況。而且有些時候,業務上我們並不需要這麼高頻繁的函式呼叫。這個時候我們就可以根據業務使用函式節流或函式防抖了。
函式節流
實現
let count = 0;
$(document).scroll(throttle(function () {
console.log(`觸發了${++count}次!`);
}, 500));
function throttle(fn, wait) {
let canRun = true;
return function () {
if (!canRun) {
return;
}
canRun = false;
setTimeout(() => {
fn.apply(this, arguments); //讓scroll的回撥函式的this保持一致
canRun = true;
}, wait);
}
}
複製程式碼
我們指定了一個時間間隔500ms,規定500ms內滾動事件回撥函式只能執行一次。函式節流的實現要點是,使用閉包儲存是否可執行標記,如果可執行則設定一個延遲函式,在延遲函式中重置可執行標記。
函式防抖
實現
let count = 0;
$(document).scroll(deBounce(function () {
console.log(`觸發了${++count}次!`);
}, 500));
function deBounce(fn, interval) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() =>{
fn.apply(this, arguments);
}, interval);
}
}
複製程式碼
我們指定一個時間間隔500ms,規定滾動事件的回撥函式,只有在事件觸發的間隔大於500ms時回撥函式才執行。函式防抖的實現要點是,使用閉包儲存一個延遲函式編號(便於清空延遲函式),在事件觸發的時候,清空這個延遲函式,並且設定新的延遲函式。
關於fn.apply(this, arguments)
上文函式節流和函式防抖的實現中,呼叫fn的時候都是用的fn.apply(this, arguments)呼叫,而不是fn()直接呼叫。主要原因是為了fn函式內的this與原本的事件回撥函式繫結的this保持一致。 setTimeout()呼叫的程式碼執行在與所在函式完全分離的執行環境上。這會導致,這些程式碼中包含的 this 關鍵字會指向 window (或全域性)物件。因此在setTimeout中使用箭頭函式(箭頭的this繫結的是當前的詞法作用域)此時的詞法作用域為scroll事件回撥函式中繫結的this(jquery中強制this指向dom元素),再將fn函式內的this繫結為這個this。我們以函式防抖為例看看這兩種的區別。
$(document).scroll(deBounce(function () {
console.log(this); //#document
}, 500));
function deBounce(fn, interval) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() =>{
fn.apply(this, arguments);
}, interval);
}
}
複製程式碼
$(document).scroll(deBounce(function () {
console.log(this); //Window
}, 500));
function deBounce(fn, interval) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() =>{
fn();
}, interval);
}
}
複製程式碼
$(document).scroll(deBounce(function () {
console.log(this); //Window
}, 500));
function deBounce(fn, interval) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(this, arguments);
}, interval);
}
}
複製程式碼
希望本文對大家有所幫助,互相學習,一起提高。轉載請註明原帖。