参考文章:【NewName】https://juejin.cn/post/7091455593340207134
debounce
防抖函数 debounce 指的是某个函数在某段时间内,无论触发了多少次回调,都只执行最后一次。
实现原理
防抖的处理可以通过setTimeout来指定一定时间后执行处理函数,如果在这之前事件再次触发,则清空计时器,重新计时,计时结束后触发函数执行。
基础版本的实现:
debounce(fn, delay = 300) {let timer;return function () {const args = arguments;if (timer) {// 清空计时器,重新计时clearTimeout(timer);}timer = setTimeout(() => {// 改变this指向为调用debounce所指的对象fn.apply(this, args);}, delay);};}
使用:
window.addEventListener("scroll",debounce(() => {console.log(111);}, 1000));
underscore 源码实现
写的功能比较全面,在实现的基础上,还包含了立刻执行,立刻执行时有返回值,取消防抖的功能。
- 先定位入口函数「debounced」,如果不存在定时器timeout,开始计时,调用later计算计时;
- 判断是否需要立刻执行,是 -> 立刻调用
计时流程:计算经过的时间,与wait比较,如果延时未结束,继续延时,且修改延时的时间;如果延时结束,执行函数(非立刻调用情况),释放 args 和 context 变量(防止内存泄漏)。
export default function debounce(func, wait, immediate) {// timeout定时器 previous当前时间var timeout, previous, args, result, context;// later的作用类似于 if (timer) clearTimeout(timer);var later = function() {var passed = now() - previous;// 当需要等待的时间 > 经过的时间,延时未结束,继续延时。if (wait > passed) {// 递归地重新计时,但延时的时长改为 wait - passed,也就是剩余需要等待的时间timeout = setTimeout(later, wait - passed);} else {timeout = null;// 执行函数(非立刻执行的情况下)if (!immediate) result = func.apply(context, args);// This check is needed because `func` can recursively invoke `debounced`.if (!timeout) args = context = null;}};// restArguments 类似于 es6的「...rest」var debounced = restArguments(function(_args) {context = this;args = _args;previous = now();if (!timeout) {// 开始计时timeout = setTimeout(later, wait);// 判断是否立刻调用if (immediate) result = func.apply(context, args);}return result;});// 取消防抖,清除定时器debounced.cancel = function() {clearTimeout(timeout);timeout = args = context = null;};return debounced;}
总结
underscore实现的功能很完善,可以多学习,手写多几遍,把功能实现都掌握的话,面试很加分。
使用闭包要注意内存泄漏问题。
