一个事件频繁触发,但是我们不想让他触发的这么频繁,于是我们就设置一个定时器让这个事件在 xxx 秒之后再执行。如果 xxx 秒内触发了,则清理定时器,重置等待事件 xxx 秒
比如在拖动 window 窗口进行 background 变色的操作的时候,如果不加限制的话,随便拖个来回会引起无限制的页面回流与重绘
或者在用户进行 input 输入的时候,对内容的验证放在用户停止输入的 300ms 后执行(当然这样不一定好,比如银行卡长度验证不能再输入过程中及时反馈)
<script>
let body = document.getElementsByTagName("body")[0];
let index = 0;
if (body) {
window.onresize = function() {
index++;
console.log("变色" + index + "次");
let num = [Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255)];
body.style.background = "rgb(" + num[0] + "," + num[1] + "," + num[2] + ")"; //晃瞎眼睛
};
}
</script>
<script>
let body = document.getElementsByTagName("body")[0];
let index = 0;
let timer = null;
if (body) {
window.onresize = function() {
//如果在一秒的延迟过程中再次触发,就将定时器清除,清除完再重新设置一个新的
clearTimeout(timer);
timer = setTimeout(function() {
index++;
console.log("变色" + index + "次");
let num = [Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255)];
body.style.background = "rgb(" + num[0] + "," + num[1] + "," + num[2] + ")";
}, 1000); //反正就是等你什么都不干一秒后才会执行代码
};
}
</script>
<script>
let body = document.getElementsByTagName("body")[0];
let index = 0;
let lazyLayout = debounce(changeBgColor, 1000);
window.onresize = lazyLayout;
function changeBgColor() {
index++;
console.log("变色" + index + "次");
let num = [Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255)];
body.style.background = "rgb(" + num[0] + "," + num[1] + "," + num[2] + ")";
}
//函数去抖(连续事件触发结束后只触发一次)
function debounce(func, wait) {
let timeout, context, args; //默认都是undefined
return function() {
context = this;
args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(function() {
//执行的时候到了
func.apply(context, args);
}, wait);
};
}
</script>
//1.9.1
_.debounce = function(func, wait, immediate) {
var timeout, result;
var later = function(context, args) {
timeout = null;
if (args) result = func.apply(context, args);
};
var debounced = restArguments(function(args) {
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
timeout = _.delay(later, wait, this, args);
}
return result;
});
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
//上面的变色在节流中就是这样写了
<script>
let doSomething = true;
let body = document.getElementsByTagName("body")[0];
let index = 0;
window.onresize = function() {
if (!doSomething) return;
doSomething = false;
setTimeout(function() {
index++;
console.log("变色" + index + "次");
let num = [Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255)];
body.style.background = "rgb(" + num[0] + "," + num[1] + "," + num[2] + ")";
doSomething = true;
}, 1000);
};
</script>
<script>
let body = document.getElementsByTagName("body")[0];
let index = 0;
let lazyLayout = throttle(changeBgColor, 1000);
window.onresize = lazyLayout;
function changeBgColor() {
index++;
console.log("变色" + index + "次");
let num = [Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255), Math.ceil(Math.random() * 255)];
body.style.background = "rgb(" + num[0] + "," + num[1] + "," + num[2] + ")";
}
//函数去抖(连续事件触发结束后只触发一次)
function throttle(func, wait) {
let context,
args,
doSomething = true;
return function() {
context = this;
args = arguments;
if (!doSomething) return;
doSomething = false;
setTimeout(function() {
//执行的时候到了
func.apply(context, args);
doSomething = true;
}, wait);
};
}
</script>
_.throttle = function(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
};
其实二者主要就是为了解决短时间内连续多次重复触发和大量的 DOM 操作的问题,来进行性能优化(重点是同时还能接着办事,并不耽误)
防抖主要是一定在 xxx 秒后执行,而节流主要是在 xxx 内执行(时间之后,时间之内)
文章写的时候用的 underscore 1.8.2 版本,实现也是参考 underscore 的源码,实现方式与 underscore 最新有些代码还是不太一样了。(功能还是相同的)
[JavaScript] 函数节流(throttle)和函数防抖(debounce)
原文:https://www.cnblogs.com/mybilibili/p/10420048.html