16

面试之手写防抖节流

 4 years ago
source link: http://www.cnblogs.com/formercoding/p/12864441.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

面试之手写防抖节流

关注前端体验或性能优化的应该有听说过 防抖节流 。那么,什么是防抖节流呢?

防抖

概念

在短时间内多次触发同一个函数,只执行最后一次。

举例:搭乘公交车的时候,陆续有不同的乘客上车,但师傅只会在最后一个乘客上车后才关门。

效果演示

防抖前

qu6ZZvF.gif

防抖后

rANJJ33.gif

应用场景

  • 表单输入验证

  • 表单输入触发搜索 ajax

  • resize/scroll/touch/mouseove 事件

实现

简单版本

function debounce(fn, wait = 1000) {
  let timer = null;

  return function debounced(...args) {
    // 重置计时器
    if (timer) clearTimeout(timer);

    // 新计时器
    timer = setTimeout(() => {
      fn(...args);
      timer = null;
    }, wait);
  };
}

可以看出 debounce 函数的实现原理就是通过计时器延迟函数执行,短时间内再次触发时重置并添加新计时器。此时的输出函数还有个缺陷,就是 this 指向 global ,我们需要让它指向原本指向的变量。

function debounce(fn, wait = 1000) {
  let timer = null;

  return function debounced(...args) {
    // 重置计时器
    if (timer) clearTimeout(timer);

    // 新计时器
    timer = setTimeout(() => {
      fn.apply(this, ...args);
      timer = null;
    }, wait);
  };
}

现在我们实现了一个简单的防抖函数。有时候我们会要求函数在第一次触发立即执行,我们来为它添加个参数。

function debounce(fn, wait = 1000, immediate = false) {
  let timer = null;

  return function debounced(...args) {
    // 重置计时器
    if (timer) clearTimeout(timer);

    // 首次立即执行
    if (immediate && !timer) {
      fn.apply(this, ...args);

      timer = setTimeout(() => {
        timer = null;
      }, wait);

      return;
    }

    // 新计时器
    timer = setTimeout(() => {
      fn.apply(this, ...args);
      timer = null;
    }, wait);
  };
}

我们还可以为其添加取消的功能。

function debounce(fn, wait = 1000, immediate = false) {
  let timer = null;

  function debounced(...args) {
    // 重置计时器
    if (timer) clearTimeout(timer);

    // 首次立即执行
    if (immediate && !timer) {
      fn.apply(this, ...args);

      timer = setTimeout(() => {
        timer = null;
      }, wait);

      return;
    }

    // 新计时器
    timer = setTimeout(() => {
      fn.apply(this, ...args);
      timer = null;
    }, wait);
  }

  debounced.cancel = () => {
    clearTimeout(timer);
    timer = null;
  };

  return debounced;
}

此时一个功能完备的 debounce 函数就完成了。

节流

概念

多次触发同一个函数,同一段时间内只执行一次。

举例:获取验证码很多都会限制 60s 的时间,在 60s 内再次获取验证码是无效,只能获取一次。下个60s才能再次获取。

效果演示

节流前

qu6ZZvF.gif

节流后

NRJbm2m.gif

应用场景

  • 编辑器语法校验

  • resize/scroll/touch/mouseove 事件

  • 表单输入联想

实现

简单版本

function throttle(fn, wait = 1000) {
  let previous = 0;

  const throttled = (...args) => {
    const now = +new Date();

    if (now - previous > wait) {
      fn.apply(this, args);
      previous = now;
    }
  };

  return throttled;
}

可以看出节流的主要原理就是利用时间差(当前和上次执行)来过滤中间过程触发的函数执行。我们现在为其添加参数来控制是否在开始时会立即触发一次,及最后一次触发是否执行。

function throttle(fn, wait, options = { leading: true, trailing: false }) {
  let timer;
  let previous = 0;

  const { leading, trailing } = options;

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

    if (leading === false && !previous) previous = now;
    if (timer) clearTimeout(timer);

    if (now - previous > wait) {
      fn.apply(this, args);
      previous = now;
    } else if (trailing) {
      // 更新timer
      timer = setTimeout(() => {
        fn.apply(this, args);
        previous = 0;
        timer = null;
      }, wait);
    }
  };

  return throttled;
}

我们还可以为其添加取消的功能。

function throttle(fn, wait, options = { leading: true, trailing: false }) {
    let timer;
    let previous = 0;

    const { leading, trailing } = options;

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

        if (leading === false && !previous) previous = now;
        if (timer) clearTimeout(timer);

        if (now - previous > wait) {
            fn.apply(this, args);
            previous = now;
        } else if (trailing) {
            // 更新timer
            timer = setTimeout(() => {
                fn.apply(this, args);
                previous = 0;
                timer = null;
            }, wait);
        }
    }
    throttled.cancel = () => {
        clearTimeout(timer);
        timer = null;
        previous = 0;
    }


    return throttled;
}

此时一个功能完备的throttle函数也完成了。

总结

防抖和节流是两个在工作中很可能会遇到的问题,弄清楚其作用和原理对技能提升和面试都会有帮助。

参考

欢迎到前端学习打卡群一起学习~516913974


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK