前端学习笔记(一): 防抖和节流

361 阅读4分钟

防抖

定义

指触发事件后在规定时间内回调函数只能执行一次,如果在规定时间内又触发了该事件,则重新开始算规定时间。

场景

两个条件:

1.如果客户连续的操作会导致频繁的事件回调(可能引起页面卡顿)。

2.客户只关心“最后一次”操作返回的结果

eg: 输入搜索联想,用户在不断输入值时,用防抖来节约请求资源。 按钮点击:收藏,点赞,心标等

原理

通过定时器将回调函数进行延时,如果在规定时间内继续回调,发现存在之前的定时器,则将该定时器清除,并重新设置定时。

防抖分为两种类型:

1. 非立即执行: 事件触发->延时->执行回调函数;
2. 立即执行: 事件触发->执行回调函数->延时
function mydebounce(fn, delay=500,immediate) {
    let timer = null;
    return function (args) {
        let that = this;
        if (timer) clearTimeout(timer);
        console.log(timer)
        if(immediate){  //立即执行
            if(!timer) {
                fn.call(that,args)
            }
            timer = setTimeout(()=>timer = null,delay)
            
        }else{   //非立即执行
            timer = setTimeout(() => fn.call(that, args), delay)
        }
       
    }
}
let debounceShotCat = mydebounce(shotCat, 5000,true)

//最后添加事件监听 回调debounceShotCat 并传入事件返回的对象
let input = document.getElementById('debounce')
input.addEventListener('keyup', function (e) {
      debounceShotCat(e.target.value,1213)
})

节流

定义

当持续触发事件时,在规定时间段内只能调用一次回调函数,如果在规定时间内又触发了该事件,则什么也不做,也不会重置定时器。

与防抖的区别: 防抖是将多次执行变为最后一次执行,节流是将多次执行变为在规定时间内只执行一次,一般不会重置定时器。

场景

  1. 客户连续频繁地触发事件;
  2. 客户不再关心最后一次操作后的结果反馈,而是在操作过程中持续的反馈。

eg:

鼠标不断点击触发,点击事件在规定时间内只触发一次(单位时间内只触发一次)

监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

原理

  1. 时间戳方式:通过闭包方式保存上一次的时间戳,然后与事件触发的时间戳比较,如果大于规定时间,则执行回调,否则就什么都不处理。 特点:一般第一次会立即执行,之后连续频繁地触发事件,也是超过了规定时间才会执行一次。最后一次触发事件,也不会执行(说明:如果你最后一次触发时间大于规定时间,这样就算不上连续频繁触发了).

  2. :原理与防抖类似.通过闭包保存上一次定时器状态.然后事件触发时,如果定时器为null(即代表此时间隔已经大于规定时间),则设置新的定时器.到时间后执行回调函数,并将定时器置为null. 特点:当第一次触发事件时,不会立即执行函数,到了规定时间后才会执行。 之后连续频繁地触发事件,也是到了规定时间才会执行一次(因为定时器)。当最后一次停止触发后,由于定时器的延时,还会执行一次回调函数(那也是上一次成功成功触发执行的回调,而不是你最后一次触发产生的)。一句话总结就是延时回调,你能看到的回调都是上次成功触发产生的,而不是你此刻触发产生的.

说明: 这两者最大的区别:是时间戳版的函数触发是在规定时间开始的时候,而定时器版的函数触发是在规定时间结束的时候

/时间戳版:

//这里fun指的就是回调函数,我就不写出来了
function throttle(fun, delay = 500) {
    let previous = 0;  //记录上一次触发的时间戳.这里初始设为0,是为了确保第一次触发产生回调
    return function(args) {
        let now = Date.now(); //记录此刻触发时的时间戳
        let that = this;
        let _args = args;
        if (now - previous > delay) {  //如果时间差大于规定时间,则触发
            fun.apply(that, _args);
            previous = now;
        }
    }
}
//定时器版:
function throttle(fun, delay = 500) {
    let timer;
    return function(args) {
        let that = this;
        let _args = args;
        if (!timer) {  //如果定时器不存在,则设置新的定时器,到时后,才执行回调,并将定时器设为null
            timer = setTimeout(function(){
                timer = null;
                fun.apply(that, _args)
            }, delay)
        }

    }
}
//时间戳+定时器版: 实现第一次触发可以立即响应,结束触发后也能有响应 (该版才是最符合实际工作需求)
//该版主体思路还是时间戳版,定时器的作用仅仅是执行最后一次回调
function throttle(fun, delay = 500) {
     let timer = null;
     let previous = 0;
     return function(args) {
             let now = Date.now();
             let remaining = delay - (now - previous); //距离规定时间,还剩多少时间
             let that = this;
             let _args = args;
             clearTimeout(timer);  //清除之前设置的定时器
              if (remaining <= 0) {
                    fun.apply(that, _args);
                    previous = Date.now();
              } else {
                    timer = setTimeout(function(){
                    fun.apply(that, _args)
            }, remaining); //因为上面添加的clearTimeout.实际这个定时器只有最后一次才会执行
              }
      }
}

作者:shotCat 链接:juejin.cn/post/684490… 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。