温顾篇 —— JS(函数防抖debounce)

温顾篇 —— JS(函数防抖debounce)

温顾篇 —— JS(函数防抖debounce)

在前端中有一些原生事件会频繁的触发从而容易造成页面的卡顿,例如:

  • window 的 resize、scroll(页面滚动加载等)
  • mousedown、mousemove
  • keyup、keydown
  • input 的 input 事件(调用接口查询等)

接下来我就用原生来简单写下js函数的防抖:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <input type="text" class="ipt">
  <script>
    function debounce(fn, delay) {
      let timer = null;
      return function (e) {
        if (timer) {
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
          // 这里最好不要直接调用fn fn(), 因为this指向window 而且event事件是undefined
          fn()
          // fn.apply(this, arguments)
        }, delay)
      }
    }
    // 获取节点
    let ipt = document.querySelector('.ipt')
    // 绑定事件触发防抖函数
    ipt.addEventListener('input', debounce(function (event) {
      console.log('this', this);
      console.log('event', event);
    },5000))
  </script>
</body>

</html>

注意:如果防抖函数中直接调用fn函数,则会出现如下this的指向问题和绑定事件的event为undefined;
温顾篇 —— JS(函数防抖debounce)
还有这里传入的函数,也不要是箭头函数,也会导致this指向window;
温顾篇 —— JS(函数防抖debounce)

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <input type="text" class="ipt">
  <script>
    function debounce(fn, delay) {
      let timer = null;
      return function (e) {
        if (timer) {
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
          // 这里最好不要直接调用fn fn(), 因为this指向window 而且event事件是undefined
          // fn()
          fn.apply(this, arguments)
        }, delay)
      }
    }
    // 获取节点
    let ipt = document.querySelector('.ipt')
    // 绑定事件触发防抖函数
    ipt.addEventListener('input', debounce(function (event) {
      console.log('this', this);
      console.log('event', event);
    },5000))
  </script>
</body>

</html>

注意:调用时 使用apply改变函数的this指向,fn.apply(this, arguments)
温顾篇 —— JS(函数防抖debounce)

解析如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <input type="text" class="ipt">
  <script>
    function debounce(fn, delay) {
      return function (e) {
        console.log('debounce 函数执行了');
      }
    }
    
    // 获取节点
    let ipt = document.querySelector('.ipt')
    // 绑定事件触发防抖函数 addEventListener调用的是函数,所以debounce需要返回一个函数
    // 这里只会返回debounce函数中的逻辑, 并不会打印 console.log('发送请求');
    ipt.addEventListener('input', debounce((event) => {
      console.log('发送请求');
    }, 5000))
  </script>
</body>

</html>

温顾篇 —— JS(函数防抖debounce)
1、如果想触发打印 console.log('发送请求'); 怎么办呢?

在return返回的函数中执行fn函数既可;

温顾篇 —— JS(函数防抖debounce)2、如果不想立即执行fn函数,怎么办呢?

这时候想到可以用个定时器来控制,这时候就可以控制什么时候执行fn函数

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <input type="text" class="ipt">
  <script>
    function debounce(fn, delay) {
      return function (e) {
        console.log('debounce 函数执行了');
        setTimeout(() => {
          fn()
        }, delay)
      }
    }
    
    // 获取节点
    let ipt = document.querySelector('.ipt')
    // 绑定事件触发防抖函数 addEventListener调用的是函数,所以debounce需要返回一个函数
    ipt.addEventListener('input', debounce((event) => {
      console.log('发送请求');
    }), 5000)
  </script>
</body>

</html>

3、如果不想调用fn函数多次呢?

判断定时器是否存在,存在则清掉之前的定时器

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <input type="text" class="ipt">
  <script>
    function debounce(fn, delay) {
      let timer = null;
      return function (e) {
        console.log('debounce 函数执行了');
        if (timer) {
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
          fn()
        }, delay)
      }
    }
    // 获取节点
    let ipt = document.querySelector('.ipt')
    // 绑定事件触发防抖函数 addEventListener调用的是函数,所以debounce需要返回一个函数
    ipt.addEventListener('input', debounce((event) => {
      console.log('发送请求');
    }, 5000))
  </script>
</body>

</html>

防抖函数如下:

function debounce(fn, delay) {
  let timer = null;
  return function (e) {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      // 这里最好不要直接调用fn fn(), 因为this指向window 而且event事件是undefined
      fn.apply(this, arguments)
    }, delay)
  }
}

// 传参不同
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      // 这里最好不要直接调用fn fn(), 因为this指向window 而且event事件是undefined
      fn.apply(this, args)
    }, delay)
  }
}
上一篇:IO读数据


下一篇:vue记录-vue中使用lodash _.debounce防抖不生效原因,解决方案