面試之手寫防抖節流
面試之手寫防抖節流
關注前端體驗或性能優化的應該有聽說過防抖
,節流
。那麼,什麼是防抖節流呢?
防抖
概念
在短時間內多次觸發同一個函數,只執行最後一次。
舉例:搭乘公交車的時候,陸續有不同的乘客上車,但師傅只會在最後一個乘客上車後才關門。
效果演示
防抖前
防抖後
應用場景
-
表單輸入驗證
-
表單輸入觸發搜索 ajax
-
resize/scroll/touch/mouseove 事件
實現
簡單版本
function debounce(fn, wait = 1000) {
let timer = null;
return function debounced(...args) {
// 重置計時器
if (timer) clearTimeout(timer);
// 新計時器
timer = setTimeout(() => {
fn(...args);
timer = null;
}, wait);
};
}
可以看出debounce
函數的實現原理就是通過計時器延遲函數執行,短時間內再次觸發時重置並添加新計時器。此時的輸出函數還有個缺陷,就是this
指向global
,我們需要讓它指向原本指向的變量。
function debounce(fn, wait = 1000) {
let timer = null;
return function debounced(...args) {
// 重置計時器
if (timer) clearTimeout(timer);
// 新計時器
timer = setTimeout(() => {
fn.apply(this, ...args);
timer = null;
}, wait);
};
}
現在我們實現了一個簡單的防抖函數。有時候我們會要求函數在第一次觸發立即執行,我們來為它添加個參數。
function debounce(fn, wait = 1000, immediate = false) {
let timer = null;
return function debounced(...args) {
// 重置計時器
if (timer) clearTimeout(timer);
// 首次立即執行
if (immediate && !timer) {
fn.apply(this, ...args);
timer = setTimeout(() => {
timer = null;
}, wait);
return;
}
// 新計時器
timer = setTimeout(() => {
fn.apply(this, ...args);
timer = null;
}, wait);
};
}
我們還可以為其添加取消的功能。
function debounce(fn, wait = 1000, immediate = false) {
let timer = null;
function debounced(...args) {
// 重置計時器
if (timer) clearTimeout(timer);
// 首次立即執行
if (immediate && !timer) {
fn.apply(this, ...args);
timer = setTimeout(() => {
timer = null;
}, wait);
return;
}
// 新計時器
timer = setTimeout(() => {
fn.apply(this, ...args);
timer = null;
}, wait);
}
debounced.cancel = () => {
clearTimeout(timer);
timer = null;
};
return debounced;
}
此時一個功能完備的debounce
函數就完成了。
節流
概念
多次觸發同一個函數,同一段時間內只執行一次。
舉例:獲取驗證碼很多都會限制 60s 的時間,在 60s 內再次獲取驗證碼是無效,只能獲取一次。下個60s才能再次獲取。
效果演示
節流前
節流後
應用場景
-
編輯器語法校驗
-
resize/scroll/touch/mouseove 事件
-
表單輸入聯想
實現
簡單版本
function throttle(fn, wait = 1000) {
let previous = 0;
const throttled = (...args) => {
const now = +new Date();
if (now - previous > wait) {
fn.apply(this, args);
previous = now;
}
};
return throttled;
}
可以看出節流的主要原理就是利用時間差(當前和上次執行)來過濾中間過程觸發的函數執行。我們現在為其添加參數來控制是否在開始時會立即觸發一次,及最後一次觸發是否執行。
function throttle(fn, wait, options = { leading: true, trailing: false }) {
let timer;
let previous = 0;
const { leading, trailing } = options;
const throttled = function (...args) {
const now = +new Date();
if (leading === false && !previous) previous = now;
if (timer) clearTimeout(timer);
if (now - previous > wait) {
fn.apply(this, args);
previous = now;
} else if (trailing) {
// 更新timer
timer = setTimeout(() => {
fn.apply(this, args);
previous = 0;
timer = null;
}, wait);
}
};
return throttled;
}
我們還可以為其添加取消的功能。
function throttle(fn, wait, options = { leading: true, trailing: false }) {
let timer;
let previous = 0;
const { leading, trailing } = options;
const throttled = function (...args) {
const now = +new Date();
if (leading === false && !previous) previous = now;
if (timer) clearTimeout(timer);
if (now - previous > wait) {
fn.apply(this, args);
previous = now;
} else if (trailing) {
// 更新timer
timer = setTimeout(() => {
fn.apply(this, args);
previous = 0;
timer = null;
}, wait);
}
}
throttled.cancel = () => {
clearTimeout(timer);
timer = null;
previous = 0;
}
return throttled;
}
此時一個功能完備的throttle函數也完成了。
總結
防抖和節流是兩個在工作中很可能會遇到的問題,弄清楚其作用和原理對技能提升和面試都會有幫助。
參考
歡迎到前端學習打卡群一起學習~516913974