面试之手写防抖节流
面试之手写防抖节流
关注前端体验或性能优化的应该有听说过防抖
,节流
。那么,什么是防抖节流呢?
防抖
概念
在短时间内多次触发同一个函数,只执行最后一次。
举例:搭乘公交车的时候,陆续有不同的乘客上车,但师傅只会在最后一个乘客上车后才关门。
效果演示
防抖前
防抖后
应用场景
-
表单输入验证
-
表单输入触发搜索 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