JS函數節流和分時函數

2020-07-16 10:05:04
函數節流和分時函數是 JS 高階函數的兩種具體應用場景,它們都是將函數作為返回值 return 到函數外部。

JS函數節流

函數節流就是降低函數被呼叫的頻率,主要是針對 DOM 事件暴露出的問題提出的一種解決方案。例如,使用 resize、mousemove、mouseover、mouseout、keydown、keyup 等事件,都會頻繁的觸發事件。如果這些事件的處理常式中包含大量耗時操作,如 Ajax 請求、資料庫查詢、DOM 遍歷等,則可能會讓瀏覽器崩潰,嚴重影響使用者體驗。

例如,在大型網點平台的導航欄中,為了減輕 mouseover 和 mouseout 移動過快給瀏覽器處理帶來的負擔,特別是減輕涉及 Ajax 呼叫給伺服器造成的極大負擔,都會進行函數節流處理。

【設計思想】

讓程式碼在間斷的情況下重複執行。

【實現方法】

使用定時器對函數進行節流。
//函數節流封裝程式碼,引數method表示要執行的函數,delay表示要延遲的時間,單位為毫秒
function throttle(mothod, delay) {
    var timer = null;  //定時器控制代碼
    return function () {  //返回節流函數
        var context = this, args = arguments;  //上下文函數和引數物件
        clearTimeout(timer);  //先清理未執行的函數
        timer = setTimeout(function () {  //重新定義定時器,記錄新的定時器控制代碼
            method.apply(context, args);  //執行預設的函數
        }, delay);
    }
}

【應用程式碼】

設計文字方塊的 keyup 事件和視窗的 resize 事件,在瀏覽器中拖動視窗,或者在文字方塊中輸入字元,然後在控制台檢視事件響應次數和速度。
<input id="search" type="text" name="search">
<script>
    function queryData(text) { console.log("搜尋:" + text); }
    var input = document.getElementById("search");
    input.addEventListener("keyup", function(event) {queryData(this.value); });
    var n = 0;  //記錄響應次數
    function f() { console.log("響應次數:" + ++n); }
    window.onresize = f;
</script>
通過觀察可以發現,在拖動改變視窗的一瞬間,resize 事件響應了幾十次。如果在文字方塊中輸入字元,keyup 事件會立即響應,等不到使用者輸入完一個單詞。

下面使用 throttle() 封裝函數,把上面的事件處理常式轉換為節流函數,同時設定延遲時間為 500 毫秒。
input.addEventListener("keyup", function(event) {
    throttle(queryData, 500) (this.value);
});
window.onresize = throttle(f, 500);
重新進行測試,會發現拖動一次視窗改變大小,僅響應一次,而在文字方塊中輸入字元時,也不會立即響應,等了半秒鐘,才顯示輸入的字元。

JS分時函數

分時函數與函數節流的設計思路相近,但應用場景略有不同。當批次操作影響到頁面效能時,如一次往頁面中新增大量 DOM 節點,顯然會給瀏覽器渲染帶來影響,極端情況下可能會出現卡頓或假死等現象。

【設計思路】

 把批次操作分批次處理,如把 1 秒鐘建立 100 個節點,改為每隔 200 毫秒建立 100 個節點等。

【實現程式碼】

var timeChunk = function (ary, fn, count) {
    var t;
    var start = function () {
        for (var i = 0; i < Math.min(count || 1, ary.length); i ++) {
            var obj = ary.shift();
            fn(obj);
        }
    }
    return function () {
        t = setInterval (function () {
            if (ary.length === 0) {  //如果全部節點都已經被建立好
                return clearInterval(t);
            }
            start();
        }, 200);  //分批執行的時間間隔,也可以用引數的形式傳入
    };
};
timeChunk 函數接收 3 個引數,第 1 個參數列示批次操作時需要用到的資料,第 2 個引數封裝了批次操作的邏輯函數,第 3 個參數列示分批操作的數量。

下面在頁面中插入 10000 個 span 元素,由於數量巨大,這裡使用分時函數進行分批操作。
var arr = [];
for (var i = 1; i < 10000; i ++) {
    var span = document.creatElement("span");
    span.style.padding = "6px 12px";
    span.innerHTML = i;
    arr.push(span);
}
var fn = function (obj) {
    document.body.appendChild(obj);
}
timeChunk(arr, fn, 100) ();