JavaScript重难点突破:防抖与节流

防抖与节流是js中最常用的优化技术,因为用户在操作页面的时候常常会有误操作与多余操作,因此使用防抖和节流的技术可以提升性能和用户体验。

防抖

顾名思义,是防止用户因为意外操作导致函数立刻执行,破坏了用户体验,应用场景有窗口调整、实时输入查询、输入检测等。

防抖的核心是延迟执行,也就是在一次操作后,等待一段时间delay,才会执行目标函数。如果在 delay 内再次触发事件,则重新计时。

按照以上的思路尝试编写一个防抖函数。

既然是延迟执行,首先想到的是定时器,定时器被调用后,经过一段时间才会真正执行被传入的函数。

接下来考虑,如何实现等待时间内再次触发事件重新计时呢?清除之前的定时器并且再次调用定时器。

  <button id="button">hellobutton>

  <script>
    const btn = document.querySelector('#button');

    // 需要执行的函数
    const sayHello = () => {
      console.log('hello');
    };

    let timer;

    // 防抖函数
    const debounce = (func) => {
      clearTimeout(timer);
      timer = setTimeout(() => {
        func();
      }, 1000);
    };

    // 绑定到按钮上
    btn.addEventListener('click', () => debounce(sayHello))
  script>

通过这种方式我们已经实现了防抖的基础功能,但这种写法依然存在缺陷。

  • 防抖函数无法复用:所有的debounce()方法共用一个timer变量,那么如何让每个调用都维护自己独立的变量呢?使用闭包。

  • 无法获取到this对象:我们期望this对象是调用者,在上面的例子中,我们希望this是button元素,但是我们的button实际指向了window(严格模式下指向undefined)。

尝试优化

<button id="button">hellobutton>
  <button id="button2">hello2button>

  <script>
    const btn = document.querySelector('#button');
    const btn2 = document.querySelector('#button2');

    // 需要执行的函数
    const sayHello = function() {
      console.log(this);
      console.log('hello')
    };


    // 防抖函数
    const debounce = (func, delay = 1000) => {
      // 使用闭包、让每个函数维护自己的timer变量
      let timer;

      // 这里不能使用箭头函数,否则this会指向window(严格模式undefined)
      return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => {
          func.apply(this, args);
        }, delay);
      };
    };

    // 绑定到按钮上
    btn.addEventListener('click', debounce(sayHello))
    btn2.addEventListener('click', debounce(function(){console.log(this)}))
  script>

接下来我们想继续进行优化,第一次按的时候要等待delay,这会导致用户体验不佳,我们想让他首次触发就立即执行。

那么应该新建一个变量,当他为true的时候就执行,再设置一个定时器,delay后将变量设置为true

    // 立即执行防抖函数
    function debounceImmediate (func, delay = 1000) {
      let timer;
      let callNow = true;
      return function (...args) {
        clearTimeout(timer);
        if (callNow) {
          func.apply(this, args);
          callNow = false;
        }
        timer = setTimeout(() => { callNow = true; }, delay);
      };
    }

节流

顾名思义,是防止用户因为频繁操作导致函数频繁调用,影响了性能,应用场景有滚动事件(scroll)、鼠标移动(mousemove)、动画渲染等。

节流的本质是固定频率执行,无论触发多频繁,函数在 interval 时间间隔内最多执行一次。

有了写节流的经验,我们可以直接写。

通过闭包的形式引用外部变量pre,每次调用函数时生成一个Date()对象记录now当前的时间,如果now-pre的差值大于定义的时间间隔,回调函数才会被调用,并且将当前的时间作为下次间隔开始的时间,等待下次调用。

    const throttle = (func,interval = 1000) => {
      let pre = 0
      return function(...args){
        const now = new Date();
        if(now - pre > interval){
          func.apply(this,args);
          pre = now;
        }
      }
    }

你可能感兴趣的:(javascript,开发语言,ecmascript)