函數節流和分時函數是 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) ();