防抖与节流(underscore)

192 阅读2分钟

防抖

事件响应(func)在一段时间(wait)后才执行,如果在这段时间内再次调用,则重新计算时间。

应用场景:

  • scrool 事件滚动触发
  • 搜索框输入查询
  • 表单验证
  • 按钮提交事件
  • 浏览器窗口缩放,resize事件等
function debounce(func, wait, immediate){
	let timeOut,result; // result 防止调用函数有return
	let debounced = function(){
    	let self = this, // 指向调用主体
        	args = arguments;
        if(timeOut) clearTimeout(timeOut);
        if(immediate){ // 立即执行
        	let callNow = !timeOut;
            timeOut = setTimeout(()=>{ // 规定时间后可以继续执行
            	timeOut = null
            },wait);
            if(callNow) result = func.call(self,...args)
        }else {
        	timeOut = setTimeout(()=>{
            	result = func.call(self,...args)
            },wait)
        }
        return result
    }
    //终止执行
    debounced.cancel = function(){
    	clearTimeout(timeOut);
        timeOut = null // timeOut变量被闭包私有化,清除。
    }
    return debounced
}

节流

如果你持续触发事件,每个一段事件,只执行一次事件。利用定时器以及时间差。

应用场景:

  • window 对象的 resize、scroll 事件
  • 拖拽时候的mousemove
  • 射击游戏中的mousedown、keydown事件
  • 文字输入、自动完成的keyup事件等

第一版 第一次触发,最后不会被调用触发函数(leading为true,trailing为false)用时间差方式实现

function throttle(func, wait, options = {}){
	let oldTime = 0, self, args, newTime
    return function (){
    	self = this;
        args = arguments;
        newTime = new Date().valueOf();//取时间戳
        if(newTime - oldTime > wait){
        	func.call(self,...args);
            oldTime = newTime
        }
    }
}

第二版 第一次不触发,最后会被调用触发函数(leading为false,trailing为true)用定时器方式实现

function throttle(func, wait, options = {}){
	let timeOut, self, args;
    return function(){
    	self = this;
        args = arguments;
        if(!timeOut){
          	timeOut = setTimeout(()=>{
            	func.call(self, ...args)
                timeOut = null
        	},wait)
        }
        
    }
    
}

第三版 第一次触发,最后也调用触发函数(leading为true,trailing为true)

function throttle(func, wait, options = {}){
	let timeOut, self, args, newTime, oldTime;
    oldTime = 0;
    return function(){
    	self = this;
        args = arguments;
        newTime = new Date().valueOf();
        if(newTime - oldTime > wait){
        	if(timeOut){
            	clearTimeout(timeOut)
                timeOut = null;
            }
        	func.call(self, ...args)
            oldTime = newTime
        };
        if(!timeOut){
        	timeOut = setTimeout(()=>{
            	oldTime = new Date().valueOf();
            	func.call(self, ...args)
                timeOut = null
            },wait)
        }
    }
}

第四版 实现leading、trailing控制

  • leading 控制第一次是否触发
  • trailing 控制最后一次是否触发

leading与trailing 不允许同时设置为false

function throttle(func, wait, options = {}){
	let timeOut, self, args, newTime,oldTime;
    oldTime = 0;
    let later = function(){
    	oldTime = new Date().valueOf();
      	func.call(self, ...args)
      	timeOut = null
    }
    return function(){
    	self = this;
        args = arguments;
        newTime = new Date().valueOf();
        if(!options.leading && oldTime == 0){
        	oldTime = newTime
        }
        if(newTime - oldTime > wait){
        	if(timeOut && options.trailing){
            	clearTimeout(timeOut)
                timeOut = null;
            }
        	func.call(self, ...args)
            oldTime = newTime
        };
        if(!timeOut && options.trailing){
        	timeOut = setTimeout(later,wait)
        }
    }
}