函数防抖
什么是防抖?
某些时候某些操作是高频触发的,但是其实触发一次就好了,比如我们短时间内多次请求接口,那么我们不应该每次点击都去请求接口,应该只做一次就好。再比如说监听输入框的输入,不应该每次都去触发监听,应该是用户完成一段输入后在进行触发。
所以,我们的思路就是:1、对于在事件被触发 N 秒后再执行的回调(延迟执行);2、如果在这 N 秒内再次触发事件,那么就重新开始计时
var oBox = document.getElementsByClassName("box")[0];
var t = null; // 保存定时器
oBox.onclick = function () {
// 每次都先清除定时器
clearTimeout(t);
// 然后立马赋值,当 1 秒频繁触发事件的时候,第 9 行是不会执行的!
t = setTimeout(function () {
console.log(1);
}, 1000);
};
这样就实现了一个简单的防抖操作,可是我觉得还是不够完美,因为当我们第一次点击oBox
的时候也会延迟执行,那我们就可以抽离一个防抖函数,实现自定义是否第一次就延迟执行。
var oBox = document.getElementsByClassName("box")[0];
oBox.onmouseover = debounce(handleEvent, 1000, true);
function handleEvent() {
console.log(1);
}
function debounce(fn, time, triggleNow) {
var t = null;
var res;
return function () {
var _self = this;
var arg = arguments;
if (t) {
// 每次都先清除定时器
clearTimeout(t);
}
// 如果 triggleNow 为 true,就表示第一次的时候不需要延迟执行
if (triggleNow) {
// 如果 t 是 null 的话,exec 就是 true
var exec = !t;
// t 立马被赋值为定时器,1 秒内再次触发事件的时候 !t 就为 false 了
t = setTimeout(function () {
// 1 秒后再把 t 赋值为 null,下次 !t 又变成 true 了
t = null;
}, time);
// 这里比 setTimeout 先执行
if (exec) {
res = fn.apply(_self, arguments);
}
} else {
// 这里和我们最开始写的片段是一样的!
t = setTimeout(function () {
res = fn.apply(_self, arguments);
}, time);
}
return res;
};
}
函数节流
函数节流和函数防抖基本类似,唯一的区别就是:防抖函数在一段时间内频繁触发事件只会执行一次,节流函数在一段时间内频繁触发事件会定时执行一次。
var oInput = document.getElementsByClassName("search")[0];
oInput.oninput = throttle(handleEvent, 1000);
function handleEvent() {
console.log("success");
}
function throttle(fn, delay) {
var t = null;
var begin = new Date().getTime(); // 调用函数的时间
return function () {
var _self = this;
var args = arguments;
var cur = new Date().getTime(); // 触发触发的时间
clearTimeout(t); // 先清除定时器
// 如果 当前时间-调用函数的时间 >= 设置的延迟时间
// 就表示时间到了,可以立即执行
if (cur - begin >= delay) {
fn.apply(_self, args);
// 将调用函数的时间重新赋值
begin = cur;
} else {
// 否则就是时间还没到,只能延迟执行
// 然后立即赋值定时器
t = setTimeout(function () {
fn.apply(_self, args);
}, delay);
}
};
}
或者也可以用一个变量来控制:
function thorttle(fn, wait) {
let timer;
return function () {
let _this = this;
let args = arguments;
// 如果 timer 为定时器的 id ,是不会进入 if 内的
if(!timer) {
timer = setTimeout(function(){
// 延迟之后 timer 变为 null
timer = null;
fn.apply(_this, args)
}, wait)
}
}
}