JavaScript函数节流(throttle)与函数去抖(debounce)

对于浏览器窗口大小改变的时候,来动态改变页面元素的大小,能够采用window的resize事件,实现代码:javascript

<script type="text/javascript">
    var n = 0;
    function resizehandler(){
        console.log(new Date().getTime());
        console.log(++n);
    }
    
    window.onresize = resizehandler;
</script>

功能可以实现,都是当咱们用拖拽的方式改变浏览器大小的时候,控制台会不断打印执行resizehandler的函数的结果。java

一次简单的拖拽会让resizehandler()函数执行不少次,实际在显示项目中resizehandler函数可能会很复杂,甚至会涉及到先后端的数据交互,因此一次拖拽执行不少次很明显是不可以接受的。后端

函数去抖

其实咱们的本意只是窗口resize后页面作一些调整就能够了,而window的resize事件并非在resize结束后才出发,具体的触发频率不是很清楚,但却在不停地调用,直到窗口大小不在变化。相似的机制还有鼠标的mousemove,都是在短期内重复触发。浏览器

在《JavaScript高级程序设计》中有专门应对此问题的函数防抖闭包

function throttle(method, context){
    clearTimeout(method.tId);
    method.tId = setTimeout(function(){
        method.call(context);
    }, 500);
}

原理很简单,利用定时器,让函数执行延迟500毫秒,在500毫秒内若是有函数又被调用则删除上一次的调用,此次调用500毫秒后执行,如此往复。这样刚才的代码能够改成:app

<script type="text/javascript">
    var n = 0;
    function resizehandler(){
        console.log(new Date().getTime());
        console.log(++n);
    }
    
    function throttle(method, context){
        clearTimeout(method.tId);
        method.tId = setTimeout(function(){
           method.call(context);
        }, 500);
    }
    
    window.onresize = function(){
        throttle(resizehandler, window);
    };
</script>

这样的话执行就没有问题了。函数

函数防抖的另外一种方法

预先设定一个执行周期,当调用动做的时刻大于等于执行周期则执行该动做,而后进入下一个周期。性能

function throttle(method, dalay){
    var timer = null;
    return function(){
        var context = this, args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function(){
            method.apply(context, args);
        }, delay);
    }
}

调用一下试试,同样的效果this

<script type="text/javascript">
    var n = 0;
    function resizehandler(){
        console.log(new Date().getTime());
        console.log(++n);
    }
    
    function throttle(method, delay){
        var timer = null;
        return function(){
            var context = this, args = arguments;
            clearTimeout(timer);
            timer = setTimeout(function(){
                method.apply(context, args);
            }, delay);
        }
    }
    
    window.onresize = throttle(resizehandler, 500); //这里由于返回函数句柄,不用包装函数了。
</script>

比较

两种方法都是利用了setTimeout,不一样的是第二种方法加入的函数延迟执行时间,这个在第一种方案中很容易也具备的功能,无非是加一个参数。spa

可是第一种方案把tId设为函数的一个变量保存,而第二种建立了闭包存储。我的以为差距不大,很喜欢第一种,简单,高效。

新需求

百度首页输入自动提示同样的东西,我在text上绑定keyup事件,每次键盘弹起的时候自动提示,可是又不想提示那么频繁,因而我用了上面方法,可是悲剧了,只有挺直输入等500毫秒才会提示,在输入过程当中根本就没有提示。看了一下代码,可不是嘛,只有用户会盲打,在500毫秒内按一下键盘,提示函数就会不断被延迟,这样只有停下来的时候才会提示,这就没有意义了。

能不能在函数节流的基础上间隔固定时间就执行一次?

函数节流

在网上搜了一下咱们能够根据第二种下发(第一种函数拓展多个变量感受有些很差)作些改动,添加一个参数做为到固定间隔必须执行。

function throttle(method, delay, duration){
    var timer = null, begin = new Date();
    return function(){
        var context = this, args = arguments, current = new Date();
        clearTimeout(timer);
        if(current-begin >= duration){
            method.apply(context, args);
            begin = current;
        } else {
            timer = setTimeout(function(){
                method.apply(context, args);
            }, delay);
        }
    }
}

这样每次咱们判断间隔了够久,要是超过设置时间则当即执行一次,以刚才的例子试一试效果

window.onresize = throttle(resizehandler, 100, 200);

这样,既没有频繁执行也没有就最后执行。

总结

对于函数节流在作动态响应用户行为方面有较大的使用频率,具体使用基础版本的函数节流仍是改动版本的还要根据业务场景进行具体分析。

throttle和debounce均是经过减小实际逻辑处理过程的执行来提升事件处理函数运行性能的手段,并无实质上减小事件的触发次数。 (逃)

image

相关文章
相关标签/搜索