需求
在日常的前端开发中,一般会遇到一些会被频繁触发的事件,比如:window.resize
,window.scroll
,mousedown
,mousemove
,keydown
,keyup
,或者输入框等。这些函数或者是事件一般会被非常频繁的触发。
方案
为了解决这个问题一般会有两种解决方案:防抖(debounce)和节流(throttle)
防抖
原理:假定时间间隔是n秒。那么就是在函数第一次被调用时延时n秒后执行。在这n秒期间之内,再次触发,则以新的事件为准
第一版实现
function deBounce(func, wait) {
var timeFlag;
return function () {
clearInterval(timeFlag);
timeFlag = setTimeout(func, wait)
}
}
第二版实现
第一版实现中会存在this指向不正确的问题
function deBounce(func, wait) {
var timeFlag;
return function () {
var context = this;
clearInterval(timeFlag);
timeFlag = setTimeout(function () {
func.apply(context)
}, wait)
}
}
或者使用bind
function deBounce(func, wait) {
var timeFlag;
return function () {
clearInterval(timeFlag);
timeFlag = setTimeout(func.bind(this), wait)
}
}
第三版实现
第二版中的实现存在传参的问题
function deBounce(func, wait) {
var timeFlag;
return function () {
var context = this;
var args = arguments
clearInterval(timeFlag);
timeFlag = setTimeout(function () {
func.apply(context, args)
}, wait)
}
}
或者bind
function deBounce(func, wait) {
var timeFlag;
return function (...args) {
clearInterval(timeFlag);
timeFlag = setTimeout(func.bind(this, ...args), wait)
}
}
或者es6
const deBounce = (func, wait) => {
let timeFlag;
return function (...args) {
clearInterval(timeFlag);
timeFlag = setTimeout(func.bind(this, ...args), wait)
}
};
第四版实现
有时会有需要立即执行的需求。
function deBounce(func, wait, immediate) {
var timeFlag;
return function () {
var context = this;
var args = arguments;
timeFlag && clearInterval(timeFlag);
if (immediate) {
var callNow = !timeFlag;
timeFlag = setTimeout(function () {
timeFlag = null
}, wait);
if (callNow) func.apply(context, args)
} else {
timeFlag = setTimeout(function () {
func.apply(context, args)
}, wait)
}
}
}
// 或者
const deBounce = (func, wait, immediate) => {
let timeFlag;
return function (...args) {
clearInterval(timeFlag);
if (immediate) {
const oldFlag = !timeFlag;
timeFlag = setTimeout(() => {
timeFlag = null
}, wait);
oldFlag && func.apply(this, args)
} else {
timeFlag = setTimeout(func.bind(this, ...args), wait)
}
}
};
第五版
第四版的函数仍然存在部分问题。就是当函数立即执行时。可能需要函数的返回值,所以修改后有下面第五版
function deBounce(func, wait, immediate) {
var timeFlag;
var deBounced = function () {
var context = this;
var args = arguments;
var result;
timeFlag && clearInterval(timeFlag);
if (immediate) {
var callNow = !timeFlag;
timeFlag = setTimeout(function () {
timeFlag = null
}, wait);
if (callNow) result = func.apply(context, args)
} else {
timeFlag = setTimeout(function () {
func.apply(context, args)
}, wait)
}
return result
};
deBounced.cancel = function () {
clearTimeout(timeout);
timeout = null;
};
return deBounced;
}
// es6
const deBounce = (func, wait, immediate) => {
let timeFlag;
const deBounced = (...args) => {
clearInterval(timeFlag);
if (immediate) {
const oldFlag = !timeFlag;
timeFlag = setTimeout(() => {
timeFlag = null
}, wait);
oldFlag && func.apply(this, args)
} else {
timeFlag = setTimeout(func.bind(this, ...args), wait)
}
};
deBounced.cancel = () => {
clearTimeout(timeFlag);
timeFlag = null;
};
return deBounced
};