Javascript函數節流

Javascript函數節流

最近在做網頁的時候有個需求,就是瀏覽器窗口改變的時候需要改一些頁面元素大小,於是乎很自然的想到了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</span><span style="background-color: #f5f5f5; color: #000000;">=</span><span style="background-color: #f5f5f5; color: #000000;">resizehandler;
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">script</span><span style="color: #0000ff;">&gt;</span>

</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);
        }
    </span><span style="color: #0000ff;">function</span><span style="color: #000000;"> throttle(method,context){
        clearTimeout(method.tId);
        method.tId</span>=setTimeout(<span style="color: #0000ff;">function</span><span style="color: #000000;">(){
            method.call(context);
        },</span>500<span style="color: #000000;">);
    }

    window.onresize</span>=<span style="color: #0000ff;">function</span><span style="color: #000000;">(){
        throttle(resizehandler,window);
    };
</span>&lt;/script&gt;</pre>
複製代碼

拖拽一下試試,果真只執行了一次

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);
        }
    </span><span style="color: #0000ff;">function</span><span style="color: #000000;"> throttle(method,delay){
        </span><span style="color: #0000ff;">var</span> timer=<span style="color: #0000ff;">null</span><span style="color: #000000;">;
        </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">function</span><span style="color: #000000;">(){
            </span><span style="color: #0000ff;">var</span> context=<span style="color: #0000ff;">this</span>, args=<span style="color: #000000;">arguments;
            clearTimeout(timer);
            timer</span>=setTimeout(<span style="color: #0000ff;">function</span><span style="color: #000000;">(){
                method.apply(context,args);
            },delay);
        }

    }

    window.onresize</span>=throttle(resizehandler,500);<span style="color: #008000;">//</span><span style="color: #008000;">因爲返回函數句柄,不用包裝函數了</span>
&lt;/script&gt;</pre>
複製代碼

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.οnresize=throttle(resizehandler,100,200);

image

果真既沒有頻繁執行也沒有就最後執行

 

13
0
« 上一篇:HTML5本地存儲——Web SQL Database
» 下一篇:HTML5本地存儲——IndexedDB(一:基本使用)
	</div>
	<div class="postDesc">posted @ <span id="post-date">2013-11-04 21:06</span> <a href="https://www.cnblogs.com/dolphinX/">謙行</a> 閱讀(<span id="post_view_count">19570</span>) 評論(<span id="post_comment_count">7</span>)  <a href="https://i.cnblogs.com/EditPosts.aspx?postid=3403821" rel="nofollow">編輯</a> <a href="#" onclick="AddToWz(3403821);return false;">收藏</a></div>
</div>
發佈了20 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章