JS手写函数防抖与函数节流


JS 手写函数防抖与函数节流

函数防抖

概念

函数防抖是在事件被触发 n 秒后再执行回调,如果在 n 秒内又被触发,则重新计时。 函数防抖多用于 input 输入框。

实现过程:

  • 箭头函数的 this 继承自父级上下文,这里指向触发事件的目标元素
  • 事件被触发时,传入 event 对象
  • 传入 leading 参数,判断是否可以立即执行回调函数,不必要等到事件停止触发后才开始执行
  • 回调函数可以有返回值,需要返回执行结果
实现
// fn->事件触发的回调,wait->防抖时间范围,leading->回调函数是否立即执行标志位
const debounce = (fn, wait = 300, leading = true) => {
  let timerId, result;
  return function (...args) {
    timerId && clearTimeout(timerId);
    // 如果是立即执行的话,第一次触发事件回调函数就会执行,后面在wiat时间内再次触发则不再执行回调函数
    if (leading) {
      if (!timerId) result = fn.apply(this, args);
      // 在等待wait时间之后才能再次触发回调函数
      timerId = setTimeout(() => (timerId = null), wait);
    } else {
      // 如果不是立即执行函数,则是再次触发时清空定时器重新计时,等到wait时间之后再执行回调函数
      timerId = setTimeout(() => (result = fn.apply(this, args)), wait);
    }
    return result;
  };
};

函数节流

概念

函数节流是指连续触发事件,但是在 n 秒中只执行一次函数,适合应用于动画相关的场景。

实现
定时器版本
const throttle = (fn, wait = 300) => {
  let timerId;
  return function (...args) {
    if (!timerId) {
      timerId = setTimeout(() => {
        timerId = null;
        return fn.apply(this, args);
      }, wait);
    }
  };
};

// test执行一次大概要10ms左右
function test(name) {
  if (name) {
    console.log(name);
  } else {
    console.log("no-name");
  }
}
const newTest = throttle(test);
// 300ms内出发多次,函数只会执行第一次
newTest("foo");
newTest("bar");
时间戳版本
const throttle = (fn, wait = 300) => {
  let prev = 0;
  return function (...args) {
    let now = +new Date();
    if (now - prev >= wait) {
      prev = now;
      return fn.apply(this, args);
    }
  };
};
// test执行一次大概要10ms左右
function test(name) {
  if (name) {
    console.log(name);
  } else {
    console.log("no-name");
  }
}
const newTest = throttle(test);
// 300ms内出发多次,函数只会执行第一次
newTest("foo");
newTest("bar");

时间戳版本和定时器版本节流函数的区别:

image.png

定时器+时间戳版本
// leading:false 表示禁用第一次执行
// trailing: false 表示禁用停止触发的回调
const throttle = (
  fn,
  wait = 300,
  {
    // 参数解构赋值
    leading = true,
    trailing = true,
  } = {}
) => {
  let prev = 0;
  let timerId;
  const later = function (args) {
    timerId && clearTimeout(timerId);
    timerId = setTimeout(() => {
      timerId = null;
      fn.apply(this, args);
    }, wait);
  };
  return function (...args) {
    let now = +new Date();
    if (!leading) return later(args);
    if (now - prev > wait) {
      fn.apply(this, args);
      prev = now;
    } else if (trailing) {
      later(args);
    }
  };
};

参考

初、中级前端应该要掌握的手写代码实现


文章作者: CassielLee
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 CassielLee !
评论
  目录