本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
1. 引言
前端中存在很多频繁的事件触发,如:resize,onChange…等,若事件处理回调函数包含网络请求等操作,那么过于频繁的IO操作会导致页面产生性能问题,因此加入防抖策略以可以在一定程度上改善页面性能。
下面就是通过阅读underscore源码,学习防抖的原理以及如何实现一个简单的防抖程序。
2. 源码
源码仓库位于: github
debounce函数
export default function debounce(func, wait, immediate) {
var timeout, previous, args, result, context;
var later = function() {
...
};
var debounced = restArguments(function(_args) {
...
});
debounced.cancel = function() {
...
};
return debounced;
}
-
首先,定义一个创建防抖器的函数,防抖器传入参数
func达到超时时间时的调用函数wait防抖超时时间immediate函数调用的时机是在开始还是结束
-
防抖器包含以下几个属性
timeout定时器previous前一时间args超时调用函数的参数result超时调用函数的返回结果context函数调用的上下文later重新计时函数debounced超时后的函数调用,以及debounced.cancel取消防抖器
-
最后返回防抖器对象
later函数
重新计时函数
var later = function() {
var passed = now() - previous;
if (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;
}
};
var passed = now() - previous;首先计算前后事件触发的时间间隔- 判断时间间隔是否小于用户设置的等待时间,是则重新设置定时器的时间为等待时间减去经过的时间(这里和我所了解到的防抖不太相同,我的理解是没到时间触发时会将超时时间重新设置为等待时间,但这里设置为等待时间减去上一次所经过的时间,所以只要有事件触发,间隔
wait设置的时间就会执行一次,比起防抖更像是我理解的节流); - 判断时间间隔大于或等于用户设置的等待时间,则将定时器置空,立即执行为
false时执行对应的函数调用, - 当计时器为空时,将参数和上下文也置为空
debounced函数
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;
});
看到这里感觉自己好像对于later函数理解有些偏差,先将这个函数理清楚再重新梳理一下
- 大致看了下
restArguments函数的作用,主要是用来处理函数调用时传递的参数,这里暂时不展开说明(其实是人太菜了,只理解到这么多😂) - 获取调用上下文
context设置值为this,设置调用参数args,获取当前时间 - 如果定时器为空,设置定时器,等待
wait时间后定时调用later;好的,从这里开始可以看到前面的理解确实出错了,真正设置定时器的是在这里,而later函数中重新设置的定时器只是为了在wait时间后执行func函数,应此将时间设置为了wait - passed,和后一段代码就是在计时开始时执行或是计时结束后执行的区别 if (immediate) result = func.apply(context, args);判断是不是立即执行,是的话则在定时器创建之后立即执行函数,并且在wait时间内不会再被触发
cancel函数
取消
debounced.cancel = function() {
clearTimeout(timeout);
timeout = args = context = null;
};
clearTimeout()取消定时器- 设置
timeout,args和context为空
3. 原理及实现
通过上面的代码,结合一些文章可以非常容易的理解到防抖的原理,暨通过一个定时器将调用的函数延时,在一段时间内再次触发时会重置定时器以便重新计时;但是原理虽然简单,但在具体的实现过程中还需要考虑更多的细节,这也就是体现一个程序经验的地方了,那对于我这种菜鸟来说如何提升自己的经验呐,最快的就莫过于读源码了,从大佬的代码中学习吸收大佬的经验。
实现
通过对上面代码的理解,自己动手实现一个简单防抖函数
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
var args = arguments;
var result;
clearTimeout(timeout);
timeout = setTimeout(function(){
result = func.apply(context, args);
}, wait);
return result;
}
}
- 这里的实现我没有添加立即执行
immediate控制条件以及取消cancel函数
4. 总结
通过underscore学习到了防抖的基本原理以及简单实现,之后还可以自己再学习一下节流相关的原理和实现。加油加油!!👍