JavaScript防抖與節(jié)流超詳細全面講解
1 為什么需要防抖和節(jié)流
在前端開發(fā)當中,有些交互事件,會被頻繁觸發(fā),這樣會導致我們的頁面渲染性能下降,如果頻繁觸發(fā)接口調用的話,會直接導致服務器性能的浪費。
舉個例子,在下面的代碼中,我們定義了一個輸入框,輸入一段文字,測試鍵盤的keyup(鍵盤彈起)事件觸發(fā)了多少次,通過該實例來演示事件是如何被頻繁觸發(fā)的。
<input type="text" id="demo"> <div>觸發(fā)了:<span id="count">0</span>次</div> <script> // 獲取input輸入框與span標簽 let demo = document.getElementById("demo"); let count = document.getElementById("count"); // 為demo輸入框注冊keyup事件 let init = 0; // 記錄keyup事件被觸發(fā)的次數 demo.onkeyup = function () { // 將span標簽中的文本修改為事件被觸發(fā)的次數 count.innerHTML = ++init; } </script>
從上面的演示可以看到,我在輸入框中輸入了5個字,但是keyup事件會被觸發(fā)30次。如果我們使用這樣的方式去檢測用戶輸入的用戶名是否可用,這樣高頻率的觸發(fā)不僅是對性能極大的浪費,而且用戶還沒有輸入完就開始檢測,對用戶來說提示并不友好。在這樣的情況下,我們就可以等用戶輸入完成之后,再去觸發(fā)函數,這樣的優(yōu)化就使用到了防抖與節(jié)流。
2 防抖與節(jié)流原理
函數防抖:在事件觸發(fā)后的 n 秒之后,再去執(zhí)行真正需要執(zhí)行的函數,如果在這 n 秒之內事件又被觸發(fā),則重新開始計時。 也就是說,如果用戶在間隔時間內一直觸發(fā)函數,那么這個防抖函數內部的真正需要執(zhí)行的函數將永遠無法執(zhí)行。
那么根據防抖的原理,我們可以嘗試想象一下上面的例子的改進措施,如果為keyup事件添加防抖函數,那么只有當keyup在一段時間內不再被觸發(fā),函數才會執(zhí)行,也就說才開始計數。
函數節(jié)流:規(guī)定好一個單位時間,觸發(fā)函數一次。如果在這個單位時間內觸發(fā)多次函數的話,只有一次是可被執(zhí)行的。想執(zhí)行多次的話,只能等到下一個周期里。
如果為keyup事件添加節(jié)流函數,那么效果就是,在一段時間內,會計數一次,然后在下一段時間內,再計數一次。
在了解防抖函數和節(jié)流函數的原理之后,接下來我們可以嘗試自己寫一個防抖與節(jié)流的函數,看看是否能達到我們預想的效果。
3 實現一個防抖函數
3.1 初步實現
根據之前的描述,在事件被觸發(fā)一段時間之后,函數才會執(zhí)行一次,那么防抖函數中我們應該為其傳入兩個參數:被執(zhí)行的函數fun
和這段時間time
。
// fun:被執(zhí)行的函數 // time:間隔的時間 function debounce(fun, time) { }
對于防抖函數來說,它的返回值應該是一個函數,因為事件觸發(fā)時接收一個函數。在該函數內部,要設計一個定時器,讓在time
時間后觸發(fā)函數fun
。
function debounce(fun, time) { return function () { // time時間后觸發(fā)函數fun setTimeout(fun, time); } }
但是上面的函數有一個問題,就是事件再次被觸發(fā)時,會出現time
時間后再執(zhí)行一次函數fun
,不能達到事件觸發(fā)完成time
時間后再執(zhí)行函數的效果,也就是說,事件會被延時觸發(fā),并不能減少觸發(fā),這是因為定時器效果進行了累加,因此我們需要取消之前的定時器,以新的定時器為準。
function debounce(fun, time) { let timer; return function () { // 取消當前的定時器效果 clearTimeout(timer); // time時間后觸發(fā)函數fun timer = setTimeout(fun, time); } }
到這里一個初步的防抖函數就完成了,接下來使用該函數改進之前的例子,具體代碼如下:
<input type="text" id="demo"> <div>觸發(fā)了:<span id="count">0</span>次</div> <script> // 獲取input輸入框與span標簽 let demo = document.getElementById("demo"); let count = document.getElementById("count"); // 防抖函數 function debounce(fun, time) { let timer; return function () { // 取消當前的定時器效果 clearTimeout(timer); // time時間后觸發(fā)函數fun timer = setTimeout(fun, time); } } // 為demo輸入框注冊keyup事件 let init = 0; // 記錄keyup事件被觸發(fā)的次數 demo.onkeyup = debounce(function () { // 將span標簽中的文本修改為事件被觸發(fā)的次數 count.innerHTML = ++init; }, 1000); </script>
3.2 this問題
從上面的效果圖來看,我輸入5個字后,1秒后keyup事件就觸發(fā)了1次,對比之前的30次,大大減少了事件的觸發(fā)頻率。但是添加防抖之后,原本函數的this指向發(fā)生了改變。原本函數的this指向了觸發(fā)事件的那個對象,但是添加防抖后this指向了window。
// 添加防抖之前打印 this demo.onkeyup = function () { console.log(this); // <input type="text" id="demo"></input> }
// 添加防抖之后打印 this demo.onkeyup = debounce(function () { console.log(this); }, 1000);
因此在防抖函數中,我們需要重新把this指回觸發(fā)事件的對象上。那防抖函數中返回的函數this指向了誰呢,我們可以打印一下:
function debounce(fun, time) { return function () { console.log(this); } }
我們發(fā)現它的this也指向了觸發(fā)事件的對象,那么接下來我們只需要讓定時器的回調函數的this指向觸發(fā)事件的對象就可以,這個過程主要使用call
函數來修改this的指向。
function debounce(fun, time) { let timer; return function () { // 將當前的this賦值給that let that = this; // 取消當前的定時器效果 clearTimeout(timer); // time時間后觸發(fā)函數fun timer = setTimeout(function () { fun.call(that); // 使用call改變函數內部的this指向 }, time); } }
3.3 event問題
解決了this指向的問題,接下來觀察事件對象event的內容,添加防抖之前,事件對象event是鍵盤事件KeyboardEvent,但是添加防抖之后,event為undefined。
// 添加防抖之前 demo.onkeyup = function (e) { console.log(e); }
// 添加防抖后 demo.onkeyup = debounce(function (e) { console.log(e); }, 1000);
同樣的操作,我們可以打印一下防抖函數返回的函數的arguments參數,發(fā)現參數中就包含了事件對象。
function debounce(fun, time) { console.log(arguments); }
那么接下來我們將這個參數傳給函數fun
就可以了,具體傳給call函數。call函數第二個參數開始接受其他的參數,因此需要使用spread運算符(…)傳遞參數。
function debounce(fun, time) { let timer; return function () { // 將當前的this賦值給that let that = this; // 獲取函數的參數 let args = arguments; // 取消當前的定時器效果 clearTimeout(timer); // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } }
3.4 立即執(zhí)行
到這一步防抖函數基本可以完成了,但是我們可以再為其添加一些功能,比如說立即執(zhí)行。當設置了立即執(zhí)行之后,第一次事件觸發(fā)后,函數fun
會立即執(zhí)行,但是第一次事件觸發(fā)后的time
時間后,函數才可以重新觸發(fā)。
我們可以傳遞第三個參數,第三個參數immediate
決定了是否立即執(zhí)行,true為是,false為否。那么代碼邏輯就可以使用if…else…語句來進行判斷。我們原本的防抖函數肯定不是立即執(zhí)行的,因此放在else語句中。
function debounce(fun, time, immediate) { let timer; return function () { let that = this; // 將當前的this賦值給that let args = arguments; // 獲取函數的參數 clearTimeout(timer); // 取消當前的定時器效果 if (immediate) { // 立即執(zhí)行代碼 } else { // 不立即執(zhí)行 // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } } }
if語句中的代碼不是簡單的fun.call(that, ...args);
就可以,因為當immediate
為true時,就會一直調用,與不加防抖沒什么區(qū)別。因此我們可以引入新的變量callNow,來記錄是否要立即執(zhí)行。
function debounce(fun, time, immediate) { let timer; return function () { // 將當前的this賦值給that let that = this; // 獲取函數的參數 let args = arguments; // 取消當前的定時器效果 clearTimeout(timer); if (immediate) { // 立即執(zhí)行 let callNow = !timer; timer = setTimeout(function () { timer = null; }, time); if (callNow) fun.call(that, ...args); } else { // 不立即執(zhí)行 // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } } }
if語句中的具體邏輯為:當immediate為true時,如果之前計時器不存在,也就是說第一次觸發(fā),那么callNow的值為true,那么代碼就會立即執(zhí)行;計時器存在,callNow就是false,不會立即執(zhí)行代碼。接下來可以在keyup事件中試驗一下:
<input type="text" id="demo"> <div>觸發(fā)了:<span id="count">0</span>次</div> <script> // 獲取input輸入框與span標簽 let demo = document.getElementById("demo"); let count = document.getElementById("count"); // 防抖函數 function debounce(fun, time, immediate) { let timer; return function () { // 將當前的this賦值給that let that = this; // 獲取函數的參數 let args = arguments; // 取消當前的定時器效果 clearTimeout(timer); if (immediate) { // 立即執(zhí)行 let callNow = !timer; timer = setTimeout(function () { timer = null; }, time); if (callNow) fun.call(that, ...args); } else { // 不立即執(zhí)行 // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } } } // 為demo輸入框注冊keyup事件 let init = 0; // 記錄keyup事件被觸發(fā)的次數 demo.onkeyup = debounce(function () { // 將span標簽中的文本修改為事件被觸發(fā)的次數 count.innerHTML = ++init; }, 1000, true); </script>
從上面效果可以看出,在輸入第一個1時,事件就立即觸發(fā)了,在接下來的1秒內事件不再被觸發(fā),而是在事件被觸發(fā)的1秒之后才可以繼續(xù)觸發(fā)。
3.5 返回值問題
如果被執(zhí)行的函數有返回值,使用上面的防抖函數就沒辦法獲取到返回值了,因此可以繼續(xù)改進:
function debounce(fun, time, immediate) { // result用來獲取返回值 let timer, result; return function () { // 將當前的this賦值給that let that = this; // 獲取函數的參數 let args = arguments; // 取消當前的定時器效果 clearTimeout(timer); if (immediate) { // 立即執(zhí)行 let callNow = !timer; timer = setTimeout(function () { timer = null; }, time); if (callNow) result = fun.call(that, ...args); } else { // 不立即執(zhí)行 // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } return result; } }
3.6 取消防抖
如果一個防抖函數等待的時間過長,immediate為true,那么我們可以取消防抖,然后再去觸發(fā),這樣就可以減少等待時間。
在代碼中我們將防抖返回的函數保存在變量debounced
中,并且為它增加一個cancel方法,通過該方法可以取消當前的定時器,從而實現取消的效果。
function debounce(fun, time, immediate) { // result用來獲取返回值 let timer, result; let debounced = function () { // 將當前的this賦值給that let that = this; // 獲取函數的參數 let args = arguments; // 取消當前的定時器效果 clearTimeout(timer); if (immediate) { // 立即執(zhí)行 let callNow = !timer; timer = setTimeout(function () { timer = null; }, time); if (callNow) result = fun.call(that, ...args); } else { // 不立即執(zhí)行 // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } return result; } debounced.cancel = function () { clearTimeout(timer); // 清除定時器 timer = null; // 閉包會導致內存泄漏,因此需要將定時器制空 } return debounced; // 返回防抖函數 }
使用keyup事件試驗一下,當沒有取消防抖時,一段時間后才可以再次觸發(fā)事件:
<input type="text" id="demo"> <div>觸發(fā)了:<span id="count">0</span>次</div> <button id="btn">取消防抖</button> <script> // 獲取input輸入框、span標簽、按鈕 let demo = document.getElementById("demo"); let count = document.getElementById("count"); let btn = document.getElementById("btn"); // 防抖代碼函數省略 // 為demo輸入框注冊keyup事件 let init = 0; // 記錄keyup事件被觸發(fā)的次數 function fun() { // 觸發(fā)keyup后要執(zhí)行的函數 count.innerHTML = ++init; } let fd = debounce(fun, 3000, true); demo.onkeyup = fd; // 為輸入框注冊keyup事件 btn.onclick = function () { // 取消防抖的效果 fd.cancel(); } </script>
當取消防抖函數之后,就可以立即觸發(fā)事件了:
3.7 總結
初步防抖函數,解決了this指向以及event參數的問題:
function debounce(fun, time) { let timer; return function () { // 將當前的this賦值給that let that = this; // 獲取函數的參數 let args = arguments; // 取消當前的定時器效果 clearTimeout(timer); // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } }
增加了立即執(zhí)行效果的防抖函數:
function debounce(fun, time, immediate) { let timer; return function () { // 將當前的this賦值給that let that = this; // 獲取函數的參數 let args = arguments; // 取消當前的定時器效果 clearTimeout(timer); if (immediate) { // 立即執(zhí)行 let callNow = !timer; timer = setTimeout(function () { timer = null; }, time); if (callNow) fun.call(that, ...args); } else { // 不立即執(zhí)行 // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } } }
解決了返回值問題的防抖函數:
function debounce(fun, time, immediate) { // result用來獲取返回值 let timer, result; return function () { // 將當前的this賦值給that let that = this; // 獲取函數的參數 let args = arguments; // 取消當前的定時器效果 clearTimeout(timer); if (immediate) { // 立即執(zhí)行 let callNow = !timer; timer = setTimeout(function () { timer = null; }, time); if (callNow) result = fun.call(that, ...args); } else { // 不立即執(zhí)行 // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } return result; } }
增加了取消功能的防抖函數:
function debounce(fun, time, immediate) { // result用來獲取返回值 let timer, result; let debounced = function () { // 將當前的this賦值給that let that = this; // 獲取函數的參數 let args = arguments; // 取消當前的定時器效果 clearTimeout(timer); if (immediate) { // 立即執(zhí)行 let callNow = !timer; timer = setTimeout(function () { timer = null; }, time); if (callNow) result = fun.call(that, ...args); } else { // 不立即執(zhí)行 // time時間后觸發(fā)函數fun timer = setTimeout(function () { // 使用call改變函數內部的this指向,并傳遞參數 fun.call(that, ...args); }, time); } return result; } debounced.cancel = function () { clearTimeout(timer); // 清除定時器 timer = null; // 閉包會導致內存泄漏,因此需要將定時器制空 } return debounced; // 返回防抖函數 }
4 實現節(jié)流函數
4.1 通過時間戳實現節(jié)流
當觸發(fā)事件的時候,我們取出當前的時間戳,然后減去之前的時間戳(時間戳初始值為0),如果大于設置的時間time
,就執(zhí)行函數fun
,然后更新時間戳為當前的時間戳,如果小于time
,就不執(zhí)行函數。
根據上面的表述,節(jié)流函數有兩個參數,一個是要執(zhí)行的函數fun
,一個是等待的時間time
,那么就可以寫出初始的代碼:
function throttle(fun, time) { // 節(jié)流代碼 }
節(jié)流函數的返回值也是一個函數,首先要設置初始時間戳為0,然后獲取當前的時間戳,如果間隔的時間大于time
,那么就執(zhí)行函數,否則不執(zhí)行。
function throttle(fun, time) { let old = 0; return function () { let now = new Date().valueOf(); // 獲取當前的時間戳 if (now - old > time) { fun(); // 執(zhí)行函數 old = now; // 更新舊時間戳 } } }
與防抖函數相同,要考慮到this指向和event改變的情況,因此引入兩個變量,使用call函數更改this指向并且重新傳入參數。
function throttle(fun, time) { let that, args; let old = 0; return function () { let now = new Date().valueOf(); // 獲取當前的時間戳 that = this; // 獲取this args = arguments; // 獲取參數 if (now - old > time) { fun.apply(that, args); // 更改this指向并傳入參數 old = now; // 更新舊時間戳 } } }
示例代碼:節(jié)流函數效果
<input type="text" id="demo"> <div>觸發(fā)了:<span id="count">0</span>次</div> <script> // 獲取input輸入框、span標簽 let demo = document.getElementById("demo"); let count = document.getElementById("count"); // 節(jié)流函數 function throttle(fun, time) { let that, args; let old = 0; return function () { let now = new Date().valueOf(); // 獲取當前的時間戳 that = this; // 獲取this args = arguments; // 獲取參數 if (now - old > time) { fun.apply(that, args); // 更改this指向并傳入參數 old = now; // 更新舊時間戳 } } } // 為demo輸入框注冊keyup事件 let init = 0; // 記錄keyup事件被觸發(fā)的次數 demo.onkeyup = throttle(function () { count.innerHTML = ++init; }, 1000); </script>
4.2 使用定時器實現節(jié)流
節(jié)流函數有兩個參數,并且返回值是一個函數,會修改this指向和event事件對象,那么它的框架就可以理出來了:
function throttle(fun, time) { let that, args; return function () { that = this; args = arguments; fun.apply(that, args); } }
函數應該在定時器的回調函數中調用,因此還需要聲明一個定時器變量timer
,當定時器不存在時,觸發(fā)定時器,調用函數。
function throttle(fun, time) { // timer是定時器對象 let that, args, timer; return function () { that = this; args = arguments; if (!timer) { timer = setTimeout(function () { fun.apply(that, args); }, time); } } }
但是當定時器timer
一旦觸發(fā),就會永遠有值,不可能再觸發(fā)定時器了,因此需要在定時器回調函數中將time
置為空。
function throttle(fun, time) { // timer是定時器對象 let that, args, timer; return function () { that = this; args = arguments; if (!timer) { timer = setTimeout(function () { timer = null; fun.apply(that, args); }, time); } } }
示例代碼:查看函數效果
<input type="text" id="demo"> <div>觸發(fā)了:<span id="count">0</span>次</div> <script> // 獲取input輸入框、span標簽、按鈕 let demo = document.getElementById("demo"); let count = document.getElementById("count"); // 節(jié)流函數 function throttle(fun, time) { // timer是定時器對象 let that, args, timer; return function () { that = this; args = arguments; if (!timer) { timer = setTimeout(function () { timer = null; fun.apply(that, args); }, time); } } } // 為demo輸入框注冊keyup事件 let init = 0; // 記錄keyup事件被觸發(fā)的次數 demo.onkeyup = throttle(function () { count.innerHTML = ++init; }, 1000); </script>
4.3 時間戳和定時器組合實現
從上面的效果可以看出,時間戳時間的節(jié)流函數,第一次輸入文本會立即觸發(fā),但是當輸入結束后就不再觸發(fā)了,定時器實現的節(jié)流函數,第一次輸入文本要等待一段時間后再觸發(fā),但是當輸入結束之后還會再觸發(fā)一遍。那么接下來實現一個第一次輸入文本會立即觸發(fā),但是輸入結束之后還會再次觸發(fā)的節(jié)流函數。
首先將兩個防抖函數合并一下:
function throttle(fun, time) { let that, args, timer; let old = 0; // 設置初始時間戳 return function () { that = this; args = arguments; let now = new Date().valueOf(); // 獲取初始的時間戳 if (now - old > time) { fun.apply(that, args); old = now; } if (!timer) { timer = setTimeout(function () { timer = null; fun.apply(that, args); }, time); } } }
這個防抖函數的時間戳和定時器同時在運行,那我們可以在定時器時間戳內部,定時器內回調函數執(zhí)行一次,就將old
的值設置為最新的時間戳。這樣就可以讓時間戳和定時器節(jié)流函數時間同步。
function throttle(fun, time) { let that, args, timer; let old = 0; // 設置初始時間戳 return function () { that = this; args = arguments; let now = new Date().valueOf(); // 獲取初始的時間戳 if (now - old > time) { fun.apply(that, args); old = now; } if (!timer) { timer = setTimeout(function () { old = new Date().valueOf(); // 將old的值設置為最新的時間戳 timer = null; fun.apply(that, args); }, time); } } }
但是不應該讓兩個同步,接下來就執(zhí)行定時器的防抖函數,在時間戳防抖函數中把定時器取消掉置為空。
function throttle(fun, time) { let that, args, timer; let old = 0; // 設置初始時間戳 return function () { that = this; args = arguments; let now = new Date().valueOf(); // 獲取初始的時間戳 if (now - old > time) { if (timer) { clearTimeout(timer); timer = null; } fun.apply(that, args); old = now; } if (!timer) { timer = setTimeout(function () { old = new Date().valueOf(); // 將old的值設置為最新的時間戳 timer = null; fun.apply(that, args); }, time); } } }
示例代碼:查看節(jié)流函數的效果
<input type="text" id="demo"> <div>觸發(fā)了:<span id="count">0</span>次</div> <script> // 獲取input輸入框、span標簽 let demo = document.getElementById("demo"); let count = document.getElementById("count"); // 節(jié)流函數省略 // 為demo輸入框注冊keyup事件 let init = 0; // 記錄keyup事件被觸發(fā)的次數 demo.onkeyup = throttle(function () { count.innerHTML = ++init; }, 1000); </script>
4.4 節(jié)流優(yōu)化
如果我們希望設計一個防抖函數,可以根據不同的情況來選擇不同的防抖函數,也就是說,對上面三種情況再進行一個結合。那么我們可以設置options
為第三個參數,根據傳的值判斷使用哪種防抖函數。options
可以有兩個參數:leading
,表示是否打開第一次執(zhí)行;trailing
:表示是否打開最后一次執(zhí)行。
function throttle(fun, time, options) { // options決定使用哪種節(jié)流效果 let that, args, timer; let old = 0; // 設置初始時間戳 if (!options) options = {}; // 如果沒有該參數,置為空對象 return function () { that = this; args = arguments; let now = new Date().valueOf(); // 獲取初始的時間戳 // leading為false,表示不打開第一次執(zhí)行 if (options.leading === false && !old) { old = now; // 這樣會將下面的時間戳節(jié)流代碼跳過 } if (now - old > time) { // 第一次回直接執(zhí)行 if (timer) { clearTimeout(timer); timer = null; } fun.apply(that, args); old = now; } // trailing為false,表示不打開最后一次執(zhí)行 if (!timer && options.trailing !== false) { // 最后一次會被執(zhí)行 timer = setTimeout(function () { old = new Date().valueOf(); // 將old的值設置為最新的時間戳 timer = null; fun.apply(that, args); }, time); } } }
示例代碼:節(jié)流函數的使用效果,打開第一次執(zhí)行和最后一次執(zhí)行
demo.onkeyup = throttle(function () { count.innerHTML = ++init; }, 1000, { leading: true, trailing: true }); // 表示打開第一次執(zhí)行和最后一次執(zhí)行
打開第一次執(zhí)行,關閉最后一次執(zhí)行:
demo.onkeyup = throttle(function () { count.innerHTML = ++init; }, 1000, { leading: true, trailing: false }); // 表示打開第一次執(zhí)行,關閉最后一次執(zhí)行
關閉第一次執(zhí)行,打開最后一次執(zhí)行:
demo.onkeyup = throttle(function () { count.innerHTML = ++init; }, 1000, { leading: false, trailing: true }); // 表示打開第一次執(zhí)行,關閉最后一次執(zhí)行
如果兩個都關閉,會出現bug,因此使用時不會將兩個都關閉。
5 應用場景
防抖應用場景:
- scroll事件滾動觸發(fā)
- 搜索框輸入查詢
- 表單驗證
- 按鈕提交事件
- 瀏覽器窗口縮放,resize事件
節(jié)流應用場景:
- DOM元素的拖拽功能實現
- 射擊游戲
- 計算鼠標的移動距離
- 監(jiān)聽scroll滾動事件
本文學習于視頻:手寫函數防抖和節(jié)流
到此這篇關于JavaScript防抖與節(jié)流超詳細全面講解的文章就介紹到這了,更多相關JavaScript防抖與節(jié)流內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
用JavaScript實現 鐵甲無敵獎門人 “開口中”猜數游戲
JavaScript在常人看來都是門出不了廳堂的小語言,僅管它沒有明星語言的閃耀,但至少網頁的閃耀還是需要它的,同時它是一門很實用的語言。2009-10-10