zoukankan      html  css  js  c++  java
  • Javascript函数节流 (转)

    https://www.cnblogs.com/dolphinX/p/3403821.html

    最近在做网页的时候有个需求,就是浏览器窗口改变的时候需要改一些页面元素大小,于是乎很自然的想到了window的resize事件,于是乎我是这么写的

    复制代码
    <!DOCTYPE html>
    <html>
    <head>
        <title>Throttle</title>
    </head>
    <body>
        <script type="text/javascript">
            n=0;
            function resizehandler(){
                console.log(new Date().getTime());
                console.log(++n);
            }
    
            window.onresize=resizehandler;
        </script>
    </body>
    </html>
    复制代码

    功能倒是实现了,但是我拖拽的方式改变浏览器窗口大小的时候看了下控制台

    image

    没错,简单的一个拖拽让我的resizeHandler()方法执行了52次,这完全不是我想要的效果,实际上我的resizeHandler()方法的代码很复杂,甚至会使用ajax向服务器发送请求,要是简单的一次改变窗口大小就要调用52次这还了得

    函数节流

    其实我的本意只是窗口resize后页面做一些调整就可以,而window的resize事件并不是在resize结束后才触发的,具体则么个频率我也不知道,但却是在不停的调用,直到窗口大小不再变化。其实类似的机制还有鼠标的mousemove,都是在短时间内重复触发。

    在《JavaScript高级程序设计》中有专门应对此问题的函数节流

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

    原理很简单,利用定时器,让函数执行延迟500毫秒,在500毫秒内如果有函数又被调用则删除上一次调用,这次调用500毫秒后执行,如此往复。这样我刚才的代码可以改为

    复制代码
    <script type="text/javascript">
            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>
    复制代码

    拖拽一下试试,果真只执行了一次

    image

    另一种做法

    网上还有一种函数节流方案,它是这么做的

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

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

    复制代码
    <script type="text/javascript">
            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>
    复制代码

    image

    比较

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

    但第一种方案把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);

    image

    果真既没有频繁执行也没有就最后执行

  • 相关阅读:
    hdu 4002 Find the maximum
    hdu 2837 坑题。
    hdu 3123
    zoj Treasure Hunt IV
    hdu 2053 Switch Game 水题一枚,鉴定完毕
    poj 1430 Binary Stirling Numbers
    hdu 3037 Saving Beans
    hdu 3944 dp?
    南阳oj 求N!的二进制表示最低位的1的位置(从右向左数)。
    fzu 2171 防守阵地 II
  • 原文地址:https://www.cnblogs.com/angdh/p/14790844.html
Copyright © 2011-2022 走看看