Skip to main content

手写函数防抖与节流

防抖

定义:在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。

实现原理

  • 防抖重在清零,打断即重置定时器;
  • 利用闭包的特性,在外层函数作用域存储一个定时器,每次执行函数时重置定时器,直到某个定时器执行

代码实现

非立即执行版:

/**
* 函数防抖-非立即执行版
* @param {function} fn
* @param {number} [wait=300]
*/
function debounce(fn, wait = 300) {
// 1.初始化定时器
let timer;

// 2.返回闭包函数,访问外层作用域变量
return function (...args) {
// 3.闭包函数执行时判断定时器是否处在,如果在保护期内则重新设置定时器
if (timer) clearTimeout(timer);

timer = setTimeout(() => {
fn(...args); // 箭头函数不需额外绑定 this
}, wait);
};
}

立即执行版:

/**
* 函数防抖-立即执行版
* @param {function} fn
* @param {number} [wait=300]
* @param {boolean} [immediate=true]
*/
function debounce(fn, wait = 300, immediate = false) {
let timer = null;

return function (...args) {
if (timer) clearTimeout(timer);

if (immediate) {
// 是否立即执行的判断条件是当前 tiemr 是否为 null
const callnow = !timer;

// 定时器作用:指定时间后重置 timer
timer = setTimeout(() => {
timer = null;
}, wait);

if (callnow) fn(args);
} else {
// 定时器作用:指定时间后执行函数
timer = setTimeout(() => {
fn(args);
}, wait);
}
};
}

节流

定义:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

实现原理

代码实现

时间戳版:

/**
* 函数节流-时间戳版
* @param {function} fn
* @param {number} [wait=300]
*/
function throttle(fn, wait) {
let previous = 0;

return function (...args) {
const now = +new Date();

// 判断两次执行时间差是否在保护时间内
if (now - previous > wait) {
fn(args);
previous = now;
}
};
}

定时器版:

/**
* 函数节流-定时器版
* @param {function} fn
* @param {number} [wait=300]
*/
function throttle(fn, wait) {
let timer = null;

return function (...args) {
if (!timer) {
// 回调执行函数并重置定时器
timer = setTimeout(() => {
fn(args);
clearTimeout(timer);
timer = null;
}, wait);
}
};
}

总结

函数的防抖与节流都是利用闭包的特性,核心思想都是在返回的闭包函数中执行待执行函数,不同的是怎么判断目标函数的执行时机。


参考: