节流防抖与延迟装饰器
debounce 与 throttle 作为装饰器怎么写?
debounce(func, ms): 每次调用先清掉上一个定时器再重新计时, 只在停止调用ms后执行最后一次, 适合输入结束后统一处理;throttle(func, ms): 首次立即执行, 之后ms内忽略调用, 冷却结束时用最后一批参数补发一次, 保证首尾都执行, 适合滚动与鼠标移动;
function throttle(func, ms) {
let frozen = false,
savedArgs,
savedThis;
function wrapper() {
if (frozen) {
savedArgs = arguments;
savedThis = this;
return;
}
frozen = true;
func.apply(this, arguments); // 首次立即转发
setTimeout(() => {
frozen = false;
if (savedArgs) wrapper.apply(savedThis, savedArgs); // 补发末次调用
savedArgs = savedThis = null;
}, ms);
}
return wrapper;
}
- 概念与另一种手写见 节流与防抖;
延迟装饰器为什么要用箭头函数?
- 目标:
delay(f, ms)返回推迟ms毫秒执行的包装器; - 难点:
setTimeout里的函数由引擎裸调用, 会丢掉包装器的this与arguments; - 解法: 内层用箭头函数, 它没有自己的
this/arguments, 于是直接取自外层包装器;
function delay(f, ms) {
return function () {
setTimeout(() => f.apply(this, arguments), ms);
};
}
delay(console.log, 1000)("test"); // 1 秒后输出 test
- 等价写法: 外层先存
const ctx = this, args = [...arguments], 再在普通函数里使用;