参考:https://github.com/mqyqingfeng/Blog/issues/22
应用:防抖主要用来解决高频事件短时间高频触发,带来的一系列的bug,全部代码和解释经过验证,结合自己的理解
高频事件:
- window 的 resize、scroll
- mousedown、mousemove
- keyup、keydown
demo:主要修改事件绑定的触发函数,一步一步深入理解
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>防抖</title>
<style>
#container {width: 100%;height: 200px;line-height: 200px;text-align:center;color: #fff;background-color: #444;font-size: 30px;}
</style>
</head>
<body>
<div id="container"></div>
<script>
var count = 1;
var container = document.getElementById('container');
function getUserAction() {
container.innerHTML = count++;
};
container.onmousemove = getUserAction;
</script>
</body>
</html>
版本一:每次次事件触发,都会清除上次事件的定时器,达到连续触发,只执行一次的效果
function debounce(func, wait) {
var timeout;
return function () {
clearTimeout(timeout)//每次次事件触发,都会清除上次事件的定时器,达到连续触发,只执行一次的效果
timeout = setTimeout(func, wait);
}
}
container.onmousemove = debounce(getUserAction, 1000);//后续这一行会省略
版本二:为了解决在getUserAction使用this,如果使用debounce,那么this就会指向window
function debounce(func, wait) {
var timeout;
return function () {
var context = this;//保存事件this
clearTimeout(timeout)
timeout = setTimeout(function () {
func.apply(context)//重新绑定this
}, wait);
}
}
版本三:重新绑定事件对象
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
var args = arguments;//获取return 函数的参数
clearTimeout(timeout)
timeout = setTimeout(function () {
func.apply(context, args)//重新调用函数时指定参数
}, wait);
}
}
版本四:立即执行,事件发生后,函数立即执行
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);//
if (immediate) {//立即执行逻辑
//--------------构成是是否判断----------------
var callNow = !timeout;
timeout = setTimeout(function () {
timeout = null;//连续事件触发,下次能执行时间会一致往后移wait,除非等待时间结束后再触发
}, wait)
//--------------构成是是否判断end----------------
if (callNow) func.apply(context, args)//是第一次触发
}
else {//非立即执行
timeout = setTimeout(function () {
func.apply(context, args)
}, wait);
}
}
}
版本四.1 返回值
function debounce(func, wait, immediate) {
var timeout, result;
return function () {
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait)
if (callNow) result = func.apply(context, args)//只能立即执行才能有返回值
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
return result;//在这里返回,非立即执行会报undefined
}
}
版本四.2 取消延长wait
function debounce(func, wait, immediate) {
var timeout, result;
var debounced = function () {//通过对象保存机制
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait)
if (callNow) result = func.apply(context, args)
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
return result;
};
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;//这一步就打断了连续触发延长wait的机制
};
return debounced;//返回对象
}
使用方法:
var count = 1;
var container = document.getElementById('container');
function getUserAction(e) {
container.innerHTML = count++;
};
var setUseAction = debounce(getUserAction, 10000, true);
container.onmousemove = setUseAction;
document.getElementById("button").addEventListener('click', function(){
setUseAction.cancel();
})