防抖与节流
,它们的作用都是为了降低事件回调函数的调用频率,从而达到优化的目的。
需要防抖与节流
??
我们一定需要防抖和节流吗??那么不使用防抖和节流会发生什么事情呢?
可以举几个栗子:
例子一:在Input输入框里,需要对用户的输入进行非法词汇判断?
对该Input进行onchange事件的监听,在用户每次有输入字符的时候,都会去调用相关接口进行判断。所以就会出现下面的情况
例子二:监听全局滚动事件,从而来实现比如,展示返回顶部、表格头部置顶,和其他改变某些元素的位置等等
监听window.onscrol
,它的事件触发频率在大多数情况下都是异常的高,在它的事件处理函数中会存在触发大量的回流重绘
,像获取document.body.scrollTop
的值,如果不增加相关限制,会造成页面卡顿,带来较差的用户体验
通过这两个例子,可以看出我们要解决的问题—就是降低事件回调函数的执行频率。而所谓的防抖与节流,可以理解为,是在反复的尝试中,摸索出的一种有代表性的方案。
代表方案——防抖
这种方案可以实现的效果是:在某个时间段内如果连续触发
事件,但是只有最后一次事件
触发了回调函数。(注意这里不是停止触发就立即执行,而是有个delay的延迟)
该方案的关键在于判断,定时器是否存在,存在就清除
function debounce(fn, delay) {
let timer = null;
return function () {
let context = this;
let args = [...arguments];
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(context, args)
}, delay);
}
}
在上面的第二个例子中,就可以这样使用
let fn = debounce(controlFn, 1000); // controlFn 具体的事件处理函数
window.addEventListener('scroll', fn);
// 或者
window.onscroll = debounce(controlFn, 1000);
代表方案——节流
效果:每隔一段时间就调用一次事件处理函数(在连续触发事件的过程中,只要到达规定的时间,就执行回调函数)
使用时间戳
function throttle(fn, delay) {
let startTime = Date.now();
return function () {
let context = this;
let args = [...arguments];
let now = Date.now();
if (now - startTime >= delay) {
fn.apply(context, args);
startTime = now;
}
}
}
使用定时器
throttle(fn, delay) {
let timer = null;
return function () {
let context = this;
let args = [...arguments];
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null;
}, delay);
}
}
}
源码
在understore.js
有这么一段源码,我们一起来学习下~
与我们前面写到的相比较,增加了immediate的参数,及相关判断是否要立即执行的逻辑
// immediate 是否立即执行
_.debounce = function(func, wait, immediate) {
var timeout, result;
var later = function(context, args) {
timeout = null;
if (args) result = func.apply(context, args);
};
var debounced = restArguments(function(args) {
if (timeout) clearTimeout(timeout); // 防抖的处理
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
timeout = _.delay(later, wait, this, args);
}
return result;
});
// 增加了取消定时器的操作
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
关于restArguments
函数,就相当于兼容完成ES6中的函数参数的结构赋值
var restArguments = function(func, startIndex) {
// func.length 代表 func函数中定义的形参的数量
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
// arguments.length - startIndex 就是 从startIndex 参数到 arguments.length 的参数数量,在es6中 (a, b, ...rest)
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length),
index = 0;
// 对rest数组进行赋值,值是从arguments的startIndex开始取的
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
// 对 function (...l), function(a, ...l), function (a, b,...l) 这三种进行特殊处理
switch (startIndex) {
case 0: return func.call(this, rest);
case 1: return func.call(this, arguments[0], rest);
case 2: return func.call(this, arguments[0], arguments[1], rest);
}
// 将arguments从0到startIndex的内容,赋值到args中
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
// 对类似function (a, b, c,...l)进行处理,args就类似于这样的形式[1, 2, 3, [4, 5, 6, 7]]
args[startIndex] = rest;
return func.apply(this, args);
};
};
在看完这个restArguments
函数的实现,我有这样的疑问,为什么要在switch中单独处理,startIndex等于0、1、2这三种情况呢
总结
防抖和节流,需要我们按照自己的需求,去选择合适的指定方式。在上述代码的基础上,可按照你的实际需要,进行相应的改造和继续完善。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!