10

函数节流 & 函数防抖

函数节流和函数防抖

函数节流和函数防抖二者很容易被混淆起来。下面贴英文原文,建议认真阅读:
Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called. As in "execute this function only if 100 milliseconds have passed without it being called".
Throttling enforces a maximum number of times a function can be called over time. As in "execute this function at most once every 100 milliseconds".

函数节流:确保函数特定的时间内至多执行一次。
函数防抖:函数在特定的时间内不被再调用后执行。

上面的概念可能还是不够清晰,下面均以“输入框输入文字触发ajax获取数据”为例,分别以防抖和节流的思想来优化,二者的区别:

输入框输入文字如下:1111111111111111111111(停顿3s继续输入)11111111111111111
函数防抖:当用户持续输入1的过程中,并不会发送ajax,当用户停止输入2s后,发送ajax请求,之后到第3s后,用户继续输入1的过程中,依旧不会发送ajax,当用户停止输入2s后,又触发ajax请求。
函数节流:当用户持续输入1的过程中(假设输入1的过程超过2s了),从你开始输入1开始计时,到第2s,发送ajax请求。函数节流与你是否停止输入无关,是一种周期性执行的策略。
一句话概括:函数节流是从用户开始输入就开始计时,而函数节流是从用户停止输入开始计时。

场景分析

函数节流(throttle)

  1. 频繁的mousemove/keydown,比如高频的鼠标移动,游戏射击类的
  2. 搜索联想(keyup)
  3. 进度条(我们可能不需要高频的更新进度)
  4. 拖拽的dragover等
  5. 高频的点击,抽奖等
  6. 无限滚动(用户向下滚动无限滚动页面,要检查滚动位置距底部多远。如果离底部进了,发ajax请求获取更多数据插入页中)

函数防抖(debounce)

  1. scroll/resize事件,浏览器改变大小,有人说是throttle
  2. 文本连续输入,ajax验证/关键字搜索

注:throttle和debounce均是通过减少实际逻辑处理过程的执行来提高事件处理函数运行性能的手段,并没有实质上减少事件的触发次数。

使用函数节流是进行前端性能优化的方法之一,例如,懒加载的实现。

实现函数防抖和函数节流

函数防抖

function debounce(func,wait){
    var timeout;
    return function(){
        var context=this;//用来保存this的正确指向
        var args=arguments;//用来保存触发的事件类型,例如keyboard event
        clearTimeout(timeout);//每次都重新开始计时
        timeout=setTimeout(function(){
            func.apply(context,args);
        },wait);
    }
}
a.onkeyup=debounce(getValue,3000);
function getValue(){
    console.log(this.value);//使用debounce调用它时,this就变为window
}

函数节流

function throttle(func, wait) {
    var timeout, context, args, result;
    var previous = 0;
 
    var later = function() {
        previous = +new Date();
        timeout = null;
        func.apply(context, args)
    };

    var throttled = function() {
        var now = +new Date();
        //下次触发 func 剩余的时间
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
         // 如果没有剩余的时间了或者你改了系统时间
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
        } else if (!timeout) {
            timeout = setTimeout(later, remaining);
        }
    };
    return throttled;
}



不能写bug啊
142 声望11 粉丝