JS防抖節流函數的實現與使用場景
一、什麼是函數防抖
概念:函數防抖(debounce),就是指觸發事件後,在 n 秒內函數隻能執行一次,如果觸發事件後在 n 秒內又觸發瞭事件,則會重新計算函數延執行時間。
1、為什麼需要函數防抖?
前端開發過程中,有一些事件,常見的例如,onresize,scroll,mousemove ,mousehover 等,會被頻繁觸發(短時間內多次觸發),不做限制的話,有可能一秒之內執行幾十次、幾百次,如果在這些函數內部執行瞭其他函數,尤其是執行瞭操作 DOM 的函數(瀏覽器操作 DOM 是很耗費性能的),那不僅會浪費計算機資源,還會降低程序運行速度,甚至造成瀏覽器卡死、崩潰。
2、函數防抖的要點
函數防抖的要點,是需要一個 setTimeout 來輔助實現,延遲運行需要執行的代碼。如果方法多次觸發,則把上次記錄的延遲執行代碼用 clearTimeout 清掉,重新開始計時。若計時期間事件沒有被重新觸發,等延遲時間計時完畢,則執行目標代碼。
3、函數防抖的實現
//HTML部分 <div> 賬戶:<input type="text" id="myinput"> </div>
//JS部分 function debounce(fun,wait=1500){//ES6語法 wait=1500 設置參數默認值,如果沒有輸入wait就會使用1500 let timeout = null return function(){ if(timeout){//如果存在定時器就清空 clearTimeout(timeout) } timeout=setTimeout(function(){ fun() },wait) } } function testUname(){ console.log("輸入結束!") } document.getElementById('myinput').addEventListener('input',debounce(testUname,1000))
上面的代碼就是防抖函數的簡單運用,隻要你每次輸入間隔大於一秒,那麼永遠不會打“印輸入結束!”,直到你停止輸入嗎,這是因為每一次的輸入都會清除上一次的計時器。
看到這裡你以為就結束瞭嗎?別急,讓我們繼續看:
//HTML部分 <div> 賬戶:<input type="text" id="myinput"> </div>
//JS部分 function debounce(fun,wait=1500){ let timeout = null return function(){ console.log(this)//<input id="myinput" type="text"> console.log(arguments)//Arguments { 0: input, … } if(timeout){//如果存在定時器就清空 clearTimeout(timeout) } timeout=setTimeout(function(){ console.log(this)//Window console.log(arguments)//Arguments { … } fun() },wait) } } function testUname(){ console.log("輸入結束!") } document.getElementById('myinput').addEventListener('input',debounce(testUname,1000))
無論是防抖還是節流,我們都要解決兩個問題,this指向和arguments。
如果沒有特殊指向,setInterval和setTimeout的回調函數中this的指向都是window。這是因為JS的定時器方法是定義在window下的。這顯然不是我們希望的,因為我們監聽的是input輸入框,所以我們希望定時器裡面的this指向input。
那麼有什麼方法可以改變this指向嗎?
一種簡單的辦法就是我們可以用參數把定時器外層函數的this和arguments保存下來。然後再通過apply改變定時器要執行的函數fun的指向。
//JS部分 function debounce(fun,wait=1500){ let timeout = null return function(){ let _this = this let arg = arguments if(timeout){//如果存在定時器就清空 clearTimeout(timeout) } timeout=setTimeout(function(){ console.log(_this)//<input id="myinput" type="text"> console.log(arg)//Arguments { 0: input, … } fun.apply(_this,arg) },wait) } }
當然,你也可以用ES6的箭頭函數新特性:箭頭函數的 this 始終指向函數定義時的 this,而非執行時。箭頭函數需要記著這句話:“箭頭函數中沒有 this 綁定,必須通過查找作用域鏈來決定其值,如果箭頭函數被非箭頭函數包含,則 this 綁定的是最近一層非箭頭函數的 this,否則,this 為 undefined”。
所以也可以這樣寫:
//JS部分 function debounce(fun,wait=1500){ let timeout = null return function(){ if(timeout){//如果存在定時器就清空 clearTimeout(timeout) } timeout=setTimeout(()=>{ console.log(this)//<input id="myinput" type="text"> console.log(arguments)//Arguments { 0: input, … } fun.apply(this,arguments) },wait) } }
4、函數防抖的使用場景
函數防抖一般用在什麼情況之下呢?一般用在,連續的事件隻需觸發一次回調的場合。具體有:
- 搜索框搜索輸入。隻需用戶最後一次輸入完,再發送請求;
- 用戶名、手機號、郵箱輸入驗證;
- 瀏覽器窗口大小改變後,隻需窗口調整完後,再執行 resize 事件中的代碼,防止重復渲染。
二、什麼是函數節流
概念: 限制一個函數在一定時間內隻能執行一次。
舉個栗子,坐火車或地鐵,過安檢的時候,在一定時間(例如10秒)內,隻允許一個乘客通過安檢入口,以配合安檢人員完成安檢工作。上例中,每10秒內,僅允許一位乘客通過,分析可知,“函數節流”的要點在於,在 一定時間 之內,限制 一個動作 隻 執行一次 。
1、函數節流的要點
主要實現思路就是通過 setTimeout 定時器,通過設置延時時間,在第一次調用時,創建定時器,先設定一個變量,然後把定時器賦值給這個變量,再寫入需要執行的函數。第二次執行這個函數時,會判斷變量是否true,是則返回。當第一次的定時器執行完函數最後會設定變量為false。那麼下次判斷變量時則為false,函數會依次運行。目的在於在一定的時間內,保證多次函數的請求隻執行最後一次調用。
這麼看是不是有點看不懂?讓我們來看代碼:
2、函數節流的實現
//JS部分 function debounce(fun,wait=1000){//定時器方案 let timer = null;//先設定一個變量 return function(){ if(!timer){//如果timer為null就進入 timer = setTimeout(function(){//然後把定時器賦值給這個變量 fun()//再寫入需要執行的函數 timer = null//第一次的定時器執行完函數最後會設定變量為false,這裡的 timer = null有兩個作用,1、開啟下一次的入口,2、清除後面的定時器 }) } } } function testUname(){ console.log(Math.random()) } document.getElementById('myinput').addEventListener('input',debounce(testUname))
同樣的,節流函數也要解決this和arguments的問題,改進後如下:
//箭頭函數寫法 function debounce(fun,wait=1000){ let timer = null return function(){ if(!timer){ timer = setTimeout(()=>{ fun.apply(this,arguments) timer = null },wait) } } } //參數保存法 function debounce(fun,wait=1000){ let timer = null return function(){ let _this = this let arg = arguments if(!timer){ timer = setTimeout(function(){ fun.apply(_this,arg) timer = null },wait) } } }
3、函數節流的使用場景
到此為止,相信各位應該對函數節流有瞭一個比較詳細的瞭解,那函數節流一般用在什麼情況之下呢?
- 懶加載、滾動加載、加載更多或監聽滾動條位置;
- 百度搜索框,搜索聯想功能;
- 防止高頻點擊提交,防止表單重復提交;
總結
到此這篇關於JS防抖節流函數的實現與使用場景的文章就介紹到這瞭,更多相關JS防抖節流函數內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!