由于我自己经常忘记防抖和节流,因此写下此文章加深记忆并方便自己以后翻阅。
防抖
- 防抖的原理:你尽管触发事件,但是我一定在事件触发的n秒之后才执行,如果你在触发事件n秒内又触发了这个事件,那我就以新的事件的时间为准,n秒之后在执行。
- 防抖函数分为非立即执行和立即执行版。
- 最常见的例子:搜索
-
- 非立即执行版
// 非立即执行就是触发事件后函数不会立即执行,而是在n秒之后才执行,如果n秒内又触发了事件,那就需要重新等待n秒
function debounce(func, wait) {
let timeout;
return function () {
// 保存this和arguments是为了让debounce函数最终返回的函数this指向不变以及依旧能够接收到event参数。
let ctx = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(ctx, args);
}, wait);
}
}
-
- 立即执行版
// 立即执行版就是触发事件后函数会立即执行,然后n秒内不触发事件才会继续执行函数的效果
function debounce(func, wait) {
let timeout;
return function () {
let ctx = this;
let args = argument;
if (timeout) clearTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(ctx, args);
}
}
实际上,我们可以将2种版本结合起来,让函数变得更加完善
function debounce(func, wait, immediate) {
let timeout;
return function () {
let ctx = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
// 如果已经执行过,不再执行
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(ctx, args);
} else {
timeout = setTimeout(function() {
func.apply(ctx, args)
}, wait)
}
}
}
节流
- 节流的原理: 如果你持续触发事件,没隔一段时间,只会执行一次事件
- 根据首次是否执行以及结束后是否执行,效果有所不同。
- 一般有两种主流的实现方式,一种是使用事件戳,一种是设置定时器
// 使用事件戳
function throttle(func, wait) {
let ctx;
let args;
let previous = 0;
return function () {
let now = +new Date();
ctx = this;
args = arguments;
if (now - previous > wait) {
func.apply(ctx, args);
previous = now;
}
}
}
// 使用定时器
function throttle(func, wait) {
let ctx, args;
let timeout;
let previous = 0;
return function () {
ctx = this;
args = arguments;
if (!timeout) {
timeout = setTimeout(function() {
timeout = null;
func.apply(ctx, args);
}, wait)
}
}
}
比较两种方式
1.第一种事件会立即执行,第二种事件会在n秒后第一次执行
2.第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件
// 结合以上两者的优势,组合一下代码
function throttle(func, wait) {
let timeout, ctx, args, result;
let previous = 0;
let after = function() {
previous = +new Date();
tmeout = null;
func.apply(ctx, args);
}
let throttled = function() {
let now = +new Date();
// 下次触发func剩余的时间
let remaining = wait - (now - previous);
ctx = this;
args = arguments;
// 如果没有剩余的时间了或者你改了系统时间
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.applyg(ctx, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
};
return throttled;
}