1.概念:
防抖:指定内只执行一次,如果指定时间内再次被触发,则重新开始计时。实现主要需要利用闭包,定时器,arguments和this指向,立即执行。
节流:隔一段时间执行一次,如果指定时间内再次被触发,则不执行,指定时间后才继续执行。实现主要需要利用闭包,定时器(隔一段时间执行一次)/时间戳(立即执行),arguments和this指向。
2.防抖
简单版:
function debounce(func, wait){
let timerId = null;
return function(){
let _This = this,
args = arguments;
if(timerId) clearTimeout(timerId);
timerId = setTimeout(function(){
func.apply(_This,args);
},wait);
}
}
立即执行:
document.addEventListener('mousemove', _.debounce(handleMouseMove, 60));
function debounce(func, wait, immadiate){
let timerId = null;
return function(){
let _This = this,
args = arguments;
if(timerId) clearTimeout(timerId);
if(immadiate){
//callNow也可表示第一次执行,第一次是true,会重新设置定时器,且func.apply(_This, args),后续为false且在指定时间内重新创建定时器,直到指定时间结束
let callNow = !timerId;
timerId = setTimeout(function(){
timerId = null;
},wait);
if(callNow) func.apply(_This,args);
}else{
timerId = setTimeout(function(){
func.apply(_This,args);
},wait);
}
}
}
3.节流
时间戳方式:
立即执行,停止触发后不会最后再执行一次
function throttle(func, wait) {
let lastCallTime = Date.now(); //也可以用new Date().valueof()
return function() {
let _This = this,
args = arguments,
curCallTime = Date.now();
//只在时间到了以后,重新执行函数且重新计时
if (curCallTime - lastCallTime >= wait) {
func.apply(_This, args);
lastCallTime = Date.now();
}
}
}
定时器方式:
不立即执行,隔段时间执行一次
function throttle(func, wait){
let timerId = null;
return function(){
let _This = this,
args = arguments;
if(!timerId){
timerId = setTimeout(function(){
func.apply(_This,args);
timerId = null;
},wait);
}
}
}
时间戳和定时器结合方式:
同时利用时间戳立即执行和定时器可以隔段时间执行的优点
function throttle(func, wait){
let lastCallTime = Date.now();
let timerId = null;
return function(){
let _This = this,
args = arguments,
curCallTime = Date.now();
if(curCallTime- lastCallTime >= wait){
if(timerId) clearTimeout(timerId);
func.apply(_This,args);
lastCallTime = Date.now();
}
if(!timerId){
lastCallTime = Date.now();
timerId = setTimeout(function(){
func.apply(_This,args);
timerId = null;
},wait);
}
}
}
第三个参数设置第一次或最后一次是否执行:
/**
* 将时间戳和定时器结合
* @param {*} func
* @param {*} wait
* @param {*} options (leading:第一次是否执行 trailing:离开时是否执行 )
* @returns
*/
function throttle(func, wait, options) {
let _This, args, timer;
let lastCallTime = Date.now();
//如果没有设置第三个参数,那么第三个参数为空对象
if (!options) options = {};
return function() {
_This = this;
//获取当前函数的实参
args = arguments;
//获得时间戳
let curTime = Date.now();
if (options.leading === false && !lastCallTime) {
lastCallTime = curTime;
}
//在首次进入的时候,能够保证立即执行。
if (curTime - lastCallTime > wait) {
if (timer) clearTimeout(timer);
func.apply(_This, args);
lastCallTime = curTime;
};
//在后续的执行过程中完成每wait时间内,执行一次。
if (!timer && options.trailing !== false) {
timer = setTimeout(function() {
lastCallTime = Date.now();
func.apply(_This, args);
timer = null;
}, wait)
}
};
}
document.addEventListener('mousemove', _.throttle(handleMouseMove, 1000, {
leading: true,
trailing: true
}));