前端面试系列-JavaScript-防抖与节流(用节流优化防抖)

开发者训练营官方
发布于 2021-3-25 17:06
浏览
1收藏

一.函数防抖
当持续触发事件时,并不执行事件处理函数,一定时间段内没有再触发事件,事件处理函数才会执行一次;如果设定的时间到来之前,又一次触发了事件,就重新开始延时。

function debounce(fn, delay) {
  // 定时器
  let timer = null
  // 将debounce处理结果当作函数返回
  return function () {
    // 保留调用时的this上下文
    let context = this
    // 保留调用时传入的参数
    let args = arguments
    // 每次事件被触发时,都去清除之前的旧定时器
    if(timer) {
        clearTimeout(timer)
    }
    // 设立新定时器
    timer = setTimeout(function () {
      fn.apply(context, args)
    }, delay)
  }
}


二、函数节流
当持续触发事件时,保证一定时间段内只调用一次事件处理函数。

1.时间戳实现

function throttle(fn, interval) {
      // last为上一次触发回调的时间
      // 对比时间戳,初始化为0则首次触发立即执行,初始化为当前时间戳则wait毫秒后触发才会执行
      let last = 0;
      // 将throttle处理结果当作函数返回
      return function () {
          // 保留调用时的this上下文
          let context = this
          // 保留调用时传入的参数
          let args = arguments
          // 记录本次触发回调的时间
          let now = Date.now();
          
          // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
          if (now - last >= interval) {
          // 如果时间间隔大于我们设定的时间间隔阈值,则执行回调
              last = now;
              fn.apply(context, args);
          }
        }
    }


2.定时器实现

function throttle(fn, wait) {
  let timeout;
  return function() {
    if (!timeout) {
      timeout = setTimeout(() => {
        timeout = null
        fn.call(this, arguments)
      }, wait)
    }
  }
}


3.用节流优化防抖(定时器+时间戳)
防抖的问题在于如果用户的操作十分频繁——他每次都不等 设置的 delay 时间结束就进行下一次操作,于是每次都为该用户重新生成定时器,回调函数被延迟了不计其数次。 频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。

用节流来优化,保证在一定时间段内会调用一次事件处理函数。

 function throttle(fn, delay) {
       // last为上一次触发回调的时间, timer是定时器
       let last = 0, timer = null
       // 将throttle处理结果当作函数返回
       
       return function () { 
         // 保留调用时的this上下文
         let context = this
         // 保留调用时传入的参数
         let args = arguments
         // 记录本次触发回调的时间
         let now = +new Date()
        // +是一元操作符,利用js隐式转换将其他类型变为数字类型
         
         // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
         if (now - last < delay) {
         // 如果时间间隔小于我们设定的时间间隔阈值,则为本次触发操作设立一个新的定时器
            clearTimeout(timer)
            timer = setTimeout(function () {
               last = now
               fn.apply(context, args)
             }, delay)
         } else {
             // 如果时间间隔超出了我们设定的时间间隔阈值,那就不等了,无论如何要反馈给用户一次响应
             last = now
             fn.apply(context, args)
         }
       }
     }


三、总结
函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。
场景:比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。


四、例子

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>
<h1> JS的节流与防抖</h1>

<body>

</body>
<script>
    // JS的节流与防抖
    //用节流优化防抖 (定时器+时间戳)

    function throttle(fn, delay) {
        let last = 0,
            timer = null
        return function () {
            // 保留调用时的this上下文
            let context = this
            // 保留调用时传入的参数
            let args = arguments
            // 记录本次触发回调的时间
            let now = +new Date()
            // +是一元操作符,利用js隐式转换将其他类型变为数字类型
            // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
            if (now - last < delay) {
                // 如果时间间隔小于我们设定的时间间隔阈值,则为本次触发操作设立一个新的定时器
                clearTimeout(timer)
                timer = setTimeout(function () {
                    last = now
                    fn.apply(context, args)
                }, delay)
            } else {
                // 如果时间间隔超出了我们设定的时间间隔阈值,那就不等了,无论如何要反馈给用户一次响应
                last = now
                fn.apply(context, args)
            }
        }
    }

    // 用新的throttle包装scroll的回调
    const better_scroll3 = throttle(() => console.log('防抖+节流,触发了滚动事件'), 1000)
    document.addEventListener('scroll', better_scroll3)
</script>

</html>

————————————————
版权声明:本文为博主「LYFlied」的原创文章

分类
1
收藏 1
回复
举报
回复
    相关推荐