快速搞定防抖节流函数

本文最后更新于:2022年9月1日 凌晨

防抖函数和节流函数的定义、具体实现、应用场景和异同点

防抖节流函数

是什么

防抖函数(debounce):n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

节流函数(throttle):n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

为什么要防抖节流函数

本质上是优化高频率执行代码的一种手段,对浪费资源的事件采取限制调用次数的限制

常见应用场景

防抖在连续的事件,只需触发一次回调的场景有:

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测
  • 窗口大小 resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

节流在间隔一段时间执行一次回调的场景有:

  • 滚动加载,加载更多或滚到底部监听
  • 搜索框,搜索联想功能

代码实现

防抖

1
2
3
4
5
6
7
8
9
10
11
12
13
function debounce(func, wait) {
let timeout;

return function () {
let context = this; // 保存this指向
let args = arguments; // 拿到event对象

clearTimeout(timeout);
timeout = setTimeout(function () {
func.apply(context, args);
}, wait);
};
}

防抖如果需要立即执行,可加入第三个参数用于判断,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function debounce(func, wait, immediate) {
let timeout;

return function () {
let context = this;
let args = arguments;

if (timeout) clearTimeout(timeout); // timeout 不为null
if (immediate) {
let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
timeout = setTimeout(function () {
timeout = null;
}, wait);
if (callNow) {
func.apply(context, args);
}
} else {
timeout = setTimeout(function () {
func.apply(context, args);
}, wait);
}
};
}

节流

利用定时器实现节流函数:

1
2
3
4
5
6
7
8
9
10
11
function throttled(fn, delay = 500) {
let timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
}
};
}

利用时间戳实现节流函数,时间会立即执行,停止触发后没办法再次执行:

1
2
3
4
5
6
7
8
9
10
function throttled(fn, delay = 500) {
let oldTime = Date.now();
return function (...args) {
let newTime = Date.now();
if (newTime - oldTime >= delay) {
fn.apply(null, args);
oldTime = Date.now();
}
};
}

将时间戳写法的特性与定时器写法的特性相结合,更精确:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function throttled(fn, delay) {
let timer = null;
let startTime = Date.now();
return function () {
let curTime = Date.now(); // 当前时间
let remaining = delay - (curTime - startTime); // 从上一次到现在,还剩下多少多余时间
let context = this;
let args = arguments;
clearTimeout(timer);
if (remaining <= 0) {
fn.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(fn, remaining);
}
};
}

总结

相同点

  • 都可以通过使用 setTimeout 实现
  • 目的都是,降低回调执行频率。节省计算资源

不同点

函数防抖 函数节流
在一段连续操作结束后,处理回调,利用 clearTimeoutsetTimeout 实现 在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能
希望一定时间连续触发的事件只在最后执行一次 希望一定时间连续触发的事件一段时间内只执行一次

参考文章

面试官:什么是防抖和节流?有什么区别?如何实现?

彻底弄懂函数防抖和函数节流 - SegmentFault 思否


快速搞定防抖节流函数
http://tach.cc/2022/04/27/快速搞定防抖节流函数/
作者
chilam
发布于
2022年4月27日
许可协议