JS的节流和防抖
函数防抖(debounce)
基本概念:在时间被触发n秒在执行回调,如果在这n秒内又被触发,则重新开始计时。
未加防抖函数的例子
function inputChange(content) {
console.log("inputContent" + content);
}
let input = document.getElementById("unDebounce");
input.addEventListener("keyup", function (e) {
inputChange(e.target.value);
});
普通防抖函数的例子
/*
fun:回调函数
dealy:延时时间
*/
function debounce(fun, delay) {
return function (args) {
let that = this;
let _args = args;
clearTimeout(fun.id);
fun.id = setTimeout(function () {
fun.call(that, _args);
}, delay);
};
}
let primaryDebounceInput = document.getElementById("primaryDebounce");
let primaryDebounceChange = debounce(inputChange, 1000);
primaryDebounceInput.addEventListener("keyup", function (e) {
primaryDebounceChange(e.target.value);
});
从该函数我们不难发现:
1、 在防抖函数中使用了闭包。使得fun会一直存在。从而可以清除之前生成的定时器。
2、这是一个延时的防抖函数。并且会不断的创建销毁定时器。比较消耗资源,并不是最优的防抖函数。
优化后的防抖函数的例子
var uDebounce = (fn, wait) => {
let timer,
startTimeStamp = 0;
let context, args;
// run函数 wait时间到了进入。
let run = (timerInterval) => {
timer = setTimeout(() => {
console.log('进入定时器')
// 设定当前时间
let now = new Date().getTime();
let interval = now - startTimeStamp;
// 最后一次点击时间和当前时间不足awit
if (interval < timerInterval) {
console.log("debounce reset", timerInterval - interval);
startTimeStamp = now;
// 再次生成一个定时器
run(wait - interval);
} else {
fn.apply(context, args);
clearTimeout(timer);
timer = null;
}
}, timerInterval);
};
return function () {
context = this;
args = arguments;
let now = new Date().getTime();
// 每次触发事件便会设定startTimeStamp的时间
startTimeStamp = now;
console.log("重新赋值startTimeStamp:" + startTimeStamp)
// 初始进入或执行完回调函数可再次进入
if (!timer) {
console.log("进入if判断")
run(wait);
}
};
};
// 从优化后的函数可以看出
1、初始会进入run()函数,在wait期间会不断刷新startTimeStamp的值,延时函数到时间执行时判断,最后一次的startTimeStamp和now是否小于wait,是的话则再次进行,由此执行的防抖不会频繁的创建销毁定时器。但是也在一定程度上创建定时器。
立即执行防抖函数的例子
// 增加前缘触发功能
var debounce = (fn, wait, immediate=false) => {
let timer, startTimeStamp=0;
let context, args;
let run = (timerInterval)=>{
timer= setTimeout(()=>{
let now = (new Date()).getTime();
let interval=now-startTimeStamp
if(interval<timerInterval){
console.log('debounce reset',timerInterval-interval);
startTimeStamp=now;
run(wait-interval); // reset timer for left time
}else{
if(!immediate){
fn.apply(context,args);
}
clearTimeout(timer);
timer=null;
}
},timerInterval);
}
return function(){
context=this;
args=arguments;
let now = (new Date()).getTime();
startTimeStamp=now; // set timer start time
if(!timer){
console.log('debounce set',wait);
if(immediate) {
fn.apply(context,args);
}
run(wait); // last timer alreay executed, set a new timer
}
}
}
新增了immediate参数,如果设置为true,那么会立即执行。
函数防抖总结:
1、防抖函数分为延时防抖和立即执行防抖,根据具体需求情况选取。
2、防抖函数可以在一定程度上防止资源浪费,减少不必要的接口请求,从而减轻服务器的压力。
3、防抖函数的实际应用场景:窗口大小改变事件onresize、联想搜索(当input框值改变时触发接口请求)等。
函数节流(throttle)
基本概念: 规定在一个单位时间内,只能触发一次函数,如果这个时间内触发多次函数,那么只有一次函数生效
延时节流函数的例子
var throttling = (fn, wait) => {
let timer;
let context, args;
let run = () => {
timer=setTimeout(()=>{
fn.apply(context,args);
clearTimeout(timer);
timer=null;
},wait);
}
return function () {
context=this;
args=arguments;
if(!timer){
console.log("throttle, set");
run();
}else{
console.log("throttle, ignore");
}
}
}
立即执行节流函数的例子
/// 增加前缘
var throttling = (fn, wait, immediate) => {
let timer, timeStamp=0;
let context, args;
let run = () => {
timer=setTimeout(()=>{
if(!immediate){
fn.apply(context,args);
}
clearTimeout(timer);
timer=null;
},wait);
}
return function () {
context=this;
args=arguments;
if(!timer){
console.log("throttle, set");
if(immediate){
fn.apply(context,args);
}
run();
}else{
console.log("throttle, ignore");
}
}
}
节流函数总结:
1、在规定时间内只触发一次回调函数。
2、实际应用场景:下拉加载更多、onscrool