Skip to content

#防抖 #节流 #八股文 两者都可以理解为延时执行,防抖是一段时间内不断的触发只执行最后一次,节流是一段时间内不断触发,会均匀的间隔执行。

举例防抖:一个按钮,不断的点击,1s内点击100次,控制只执行最后一次的点击事件。

举例节流:监听鼠标移动时间,鼠标1s内移动1万次,控制只执行10次,100ms执行一次。

手写防抖函数

JS 保姆级贴心,从零教你手写实现一个防抖debounce方法 - 知乎 (zhihu.com)

html
<!DOCTYPE html>
<html lang="zh-CN">
  <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>
    <script
      src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/lodash.js/4.17.21/lodash.js"
      type="application/javascript"
    ></script>
  </head>
  <body>
    <input type="text" class="input" />
    <br />
    <span class="name"></span>
    <script>
      const span = document.querySelector('.name')
      const input = document.querySelector('.input')
      const changeName = function (e) {
        console.log(this)
        span.innerHTML = e.target.value
      }

      const debounce = function (fn, wait, immediate) {
        // 自由变量,debounce执行完成被释放,time也不会被释放
        let time
        // 返回一个闭包(...args表示接收剩余的参数)
        return function (...args) {
          // 保存闭包被调用时的this
          const this_ = this
          // 清除上一次的定时器
          if (time) {
            clearInterval(time)
          }
          if (immediate) {
            const action = !time
            // 不再是直接执行fn,在内部传递参数
            // 箭头函数的this永远指向外层作用域的this
            time = setTimeout(function () {
              // 通过apply修改fn的this
              // 定时器这种回调的执行默认就是指向window,这就导致fn.apply的this也是window,所以你必须得手动将闭包的this存起来赋予给fn,这就是为什么要保存this的缘故。
              fn.apply(this_, args)
              time = null
            }, wait)
            if (action) {
              fn.apply(this_, args)
            } 
          }
          else {
              // 不再是直接执行fn,在内部传递参数
              time = setTimeout(function () {
                // 通过apply修改fn的this
                fn.apply(this_, args)
              }, wait)
            }
        }
      }

      // 通过闭包,得到一个方法
      const changeName_ = debounce(changeName, 1000, true)

      input.onkeyup = changeName_
    </script>
  </body>
</html>

疑问

JavaScript
// 不防抖
btn.addEventListener('click',function(){
  debounce2(getImg, 2000)()
})
// 防抖
btn.onclick = debounce2(getImg, 2000)()

为什么呢

防抖2

JavaScript
function debounce (callback, delay = 1000) {
    let timer = null;
    return function () {
        // 因为这里返回出去的方法是被 外部元素调用的, 
        // 所以这里的 this 就指向了外部元素
        let self = this;
        // 每个函数内部都一个属性 arguments, 其中包括了所有外部调用这个函数传递进来的参数
        let args = arguments;
        timer && clearTimeout(timer)
        timer = setTimeout(function () {
            // apply: 作用就是改变方法内部this的指向, 并能将参数传递给该方法, 最后立即执行这个函数
            callback.apply(self, args)
        }, delay);
    }
}

// 使用
let onInput = document.queryInput('input');
onInput = debounce (function(args){
    console.log(this)
    console.log(args)
}, 2000)