节流(throttle)与防抖(debounce)

94 阅读2分钟

①因频繁执行 DOM 操作,资源加载等行为,导致 UI 停顿甚至浏览器崩溃。例如:window 对象频繁的 onresize,onscroll 等事件、拖拽的 mousemove 事件、射击游戏的 mousedown,keydown 事件、文字输入,自动完成的 keyup 事件;
②实际上对于 window 的 resize 事件,实际需求大多为停止改变大小 n 毫秒后执行后续处理;而其他事件大多数的需求是以一定的频率执行后续处理。针对这两种需求出现了 debounce(函数去抖)和 throttle(函数节流)两种方式;
③节流就是每隔 n 的时间调用一次函数,而不是一触发事件就调用一次,这样就会减少资源浪费;
④防抖就是指触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间;
⑤节流与防抖的前提都是某个行为持续地触发,不同之处只要判断是要优化到减少它的执行次数还是只执行一次就行;
⑥防抖分为立即防抖和非立即防抖。
非立即防抖:触发事件后函数不会立即执行,而是在 n 秒之后执行,如果 n 秒之内又触发了事件,则会重新计算函数执行时间;
立即防抖:触发事件后函数会立即执行,然后 n 秒内不触发事件才会继续执行函数的效果;
两种防抖结合版代码如下:
_.debounce = function (func, wait, immediate) {
var timeout, result;
var later = function (context, args) {
timeout = null;
if (args) result = func.apply (context, args);
};

var debounced = restArgs (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;
};
⑦节流分为时间戳和定时版本,时间戳版和定时器版的节流函数的区别就是,时间戳版的函数触发是在时间段内开始的时候,而定时器版的函数触发是在时间段内结束的时候;
两种节流结合版代码如下:
_.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;
};
(参考:www.jianshu.com/p/566c66aaf…