「如何稀釋scroll事件」引出的問題

背景:我在segmentfault提了個問題如何稀釋onscroll事,問題以下:javascript

面試時問到這個問題,是這樣的:
    面試官問一個關於滾動到某個位置的時候出現一個頂部的導航欄,答完以後,她接着問一滾動onscroll就會執行不少不少次,如何稀釋它?爲了肯定她說的是「稀釋」,我讓她重複了遍,我給出的解決方法是,用一個變量,在事件處理的時候讓它自增,判斷達到必定大小就執行一次實際的事件:java

var i = 0; // 累積變量 window.onscroll = function(){ i++; if(i%500==0){ // 執行實際的事件 } } 
  • 她並不滿意,問最後如何釋放這個變量?
  • ……
  • 接着她說:「我要的是稀釋onscroll的執行次數,而不是這個(我所指的實際的事件)的執行次數。」
  • 我非常不解,鼠標一滾動就觸發這個事件,如何能減小它的執行次數,如何稀釋它?難道動態綁定/解綁事件,如何操做?

    網上都沒有找到相關類型的問題,這個問題算不算變態級的面試題?若是不是,請給出解決方案,先謝謝了。git

第一個回答認爲這是函數節流,頓時恍然大悟,納爲答案,這是我和該名詞的初次接觸,本覺得就這樣結束了,沒想到接下來又吸引了一大波程序員的眼球。程序員

 

基本地,指出使用 throttle 和 debounce 兩種方式:github

throttle(譯:節流閥):就是函數節流的意思,控制函數調用的頻度,固定時間間隔執行,即連續的調用中,無論頻度如何,只間隔固定時間(或大於該時間,這時頻度較低)執行一次,不是問題中的「稀釋」。面試

debounce(譯:防反跳?):就是去抖的意思,空閒控制,在必定空閒時間間隔內的調用不予實現,一個簡單的實現以下:segmentfault

 1 var timer = null;
 2 document.addEventListener('mousemove', function () {
 3         if (timer) {
 4             clearTimeout(timer)
 5         }
 6         timer = setTimeout(function(){
 7             console.log("mousemove");
 8         }, 100);
 9     }
10 );

主要應對高度頻發的調用,100的意思不是100ms執行一次,而是當調用間隔時間不超過100ms,即鼠標移動速度過快的話,console.log()會一直不被執行,除非移動間隔時間大於100ms,用網友bumfod的話,「函數節流讓一個函數只有在你不斷觸發後停下來歇會纔開始執行,中間你操做得太快它直接無視你。」,不是問題中的「稀釋」。瀏覽器

綜上,誠然 throttle 和 debounce 都能很好地解決性能問題,兩者稀釋的是業務邏輯的執行次數,但都不是問題所要求的,這時我就以爲這個問題有點牽強了,由於無論怎樣,無論有沒有顯性地定義 scroll 事件,瀏覽器都會觸發 scroll 事件的,差異在於有沒有 callback,有沒有要執行的東西而已。app

若是非要「減小scroll 的執行次數」,這裏有一位和我不謀而合的網友代碼,經過setTimeout,執行一次再延時從新綁定事件監聽器,這種方法稀釋的是回調的執行次數框架

 1 var cb = {
 2   onscroll:function() {
 3     console.log("scrolling");
 4     window.removeEventListener("scroll", cb.onscroll, false);   // 這裏移除事件監聽器
 5     setTimeout(function() {
 6         console.log("DONE");
 7         window.addEventListener("scroll", cb.onscroll, false);
 8     }, 200);    // 200ms後從新綁定事件監聽器
 9   }
10 };
11 window.addEventListener("scroll", cb.onscroll, false);

還有同窗引出了阻塞渲染、影響頁面UI響應等的問題,

 

其餘:

框架輔助 _debounce(underscore.js 裏的 debounce 函數)

 1 /**
 2  * [debounce description]
 3  * @param  {[type]} func      [回調函數]
 4  * @param  {[type]} wait      [等待時長]
 5  * @param  {[type]} immediate [是否當即執行]
 6  * @return {[type]}           [description]
 7  */
 8 _.debounce = function(func, wait, immediate) {
 9     var timeout, args, context, timestamp, result;
10 
11     var later = function() {
12         var last = _.now() - timestamp;
13 
14         //小於wait時間,繼續延遲wait-last執行later,知道last >= wait才執行func
15         if (last < wait && last > 0) {
16             timeout = setTimeout(later, wait - last);
17         } else {
18             timeout = null;
19             if (!immediate) {
20                 result = func.apply(context, args);
21 
22                 if (!timeout) context = args = null;
23             }
24         }
25     };
26 
27     return function() {
28         context = this;
29         args = arguments;
30         timestamp = _.now();
31         //是否當即執行
32         var callNow = immediate && !timeout;
33 
34         if (!timeout) timeout = setTimeout(later, wait);
35 
36         if (callNow) {
37             result = func.apply(context, args);
38             context = args = null;
39         }
40 
41         return result;
42     };
43 };
underscore.js 的 debounce

typeahead 的 throttle 實現源碼:

 1 throttle: function(func, wait) {
 2     var context, args, timeout, result, previous, later;
 3     previous = 0;
 4     later = function() {
 5         previous = new Date();
 6         timeout = null;
 7         result = func.apply(context, args);
 8     };
 9     return function() {
10         var now = new Date(),
11             remaining = wait - (now - previous);
12         context = this;
13         args = arguments;
14         if (remaining <= 0) {   //若是大於間隔時間(wait)
15             clearTimeout(timeout);
16             timeout = null;
17             previous = now;
18             result = func.apply(context, args);
19         } else if (!timeout) {  //小於,延時調用later
20             timeout = setTimeout(later, remaining);
21         }
22         return result;
23     };
24 },
typeahead.js 的 throttle

 

若有紕漏,懇請指出,共同進步,謝謝^_^

相關文章
相關標籤/搜索