- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) > web開(kāi)發(fā) > JavaScript >
- JS防抖節流函數的實(shí)現與使用場(chǎng)景
概念:函數防抖(debounce),就是指觸發(fā)事件后,在 n 秒內函數只能執行一次,如果觸發(fā)事件后在 n 秒內又觸發(fā)了事件,則會(huì )重新計算函數延執行時(shí)間。
前端開(kāi)發(fā)過(guò)程中,有一些事件,常見(jiàn)的例如,onresize,scroll,mousemove ,mousehover 等,會(huì )被頻繁觸發(fā)(短時(shí)間內多次觸發(fā)),不做限制的話(huà),有可能一秒之內執行幾十次、幾百次,如果在這些函數內部執行了其他函數,尤其是執行了操作 DOM 的函數(瀏覽器操作 DOM 是很耗費性能的),那不僅會(huì )浪費計算機資源,還會(huì )降低程序運行速度,甚至造成瀏覽器卡死、崩潰。
函數防抖的要點(diǎn),是需要一個(gè) setTimeout 來(lái)輔助實(shí)現,延遲運行需要執行的代碼。如果方法多次觸發(fā),則把上次記錄的延遲執行代碼用 clearTimeout 清掉,重新開(kāi)始計時(shí)。若計時(shí)期間事件沒(méi)有被重新觸發(fā),等延遲時(shí)間計時(shí)完畢,則執行目標代碼。
//HTML部分 <div> 賬戶(hù):<input type="text" id="myinput"> </div>
//JS部分 function debounce(fun,wait=1500){//ES6語(yǔ)法 wait=1500 設置參數默認值,如果沒(méi)有輸入wait就會(huì )使用1500 let timeout = null return function(){ if(timeout){//如果存在定時(shí)器就清空 clearTimeout(timeout) } timeout=setTimeout(function(){ fun() },wait) } } function testUname(){ console.log("輸入結束!") } document.getElementById('myinput').addEventListener('input',debounce(testUname,1000))
上面的代碼就是防抖函數的簡(jiǎn)單運用,只要你每次輸入間隔大于一秒,那么永遠不會(huì )打“印輸入結束!”,直到你停止輸入嗎,這是因為每一次的輸入都會(huì )清除上一次的計時(shí)器。
看到這里你以為就結束了嗎?別急,讓我們繼續看:
//HTML部分 <div> 賬戶(hù):<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){//如果存在定時(shí)器就清空 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))
無(wú)論是防抖還是節流,我們都要解決兩個(gè)問(wèn)題,this指向和arguments。
如果沒(méi)有特殊指向,setInterval和setTimeout的回調函數中this的指向都是window。這是因為JS的定時(shí)器方法是定義在window下的。這顯然不是我們希望的,因為我們監聽(tīng)的是input輸入框,所以我們希望定時(shí)器里面的this指向input。
那么有什么方法可以改變this指向嗎?
一種簡(jiǎn)單的辦法就是我們可以用參數把定時(shí)器外層函數的this和arguments保存下來(lái)。然后再通過(guò)apply改變定時(shí)器要執行的函數fun的指向。
//JS部分 function debounce(fun,wait=1500){ let timeout = null return function(){ let _this = this let arg = arguments if(timeout){//如果存在定時(shí)器就清空 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 始終指向函數定義時(shí)的 this,而非執行時(shí)。箭頭函數需要記著(zhù)這句話(huà):“箭頭函數中沒(méi)有 this 綁定,必須通過(guò)查找作用域鏈來(lái)決定其值,如果箭頭函數被非箭頭函數包含,則 this 綁定的是最近一層非箭頭函數的 this,否則,this 為 undefined”。
所以也可以這樣寫(xiě):
//JS部分 function debounce(fun,wait=1500){ let timeout = null return function(){ if(timeout){//如果存在定時(shí)器就清空 clearTimeout(timeout) } timeout=setTimeout(()=>{ console.log(this)//<input id="myinput" type="text"> console.log(arguments)//Arguments { 0: input, … } fun.apply(this,arguments) },wait) } }
函數防抖一般用在什么情況之下呢?一般用在,連續的事件只需觸發(fā)一次回調的場(chǎng)合。具體有:
概念: 限制一個(gè)函數在一定時(shí)間內只能執行一次。
舉個(gè)栗子,坐火車(chē)或地鐵,過(guò)安檢的時(shí)候,在一定時(shí)間(例如10秒)內,只允許一個(gè)乘客通過(guò)安檢入口,以配合安檢人員完成安檢工作。上例中,每10秒內,僅允許一位乘客通過(guò),分析可知,“函數節流”的要點(diǎn)在于,在 一定時(shí)間 之內,限制 一個(gè)動(dòng)作 只 執行一次 。
主要實(shí)現思路就是通過(guò) setTimeout 定時(shí)器,通過(guò)設置延時(shí)時(shí)間,在第一次調用時(shí),創(chuàng )建定時(shí)器,先設定一個(gè)變量,然后把定時(shí)器賦值給這個(gè)變量,再寫(xiě)入需要執行的函數。第二次執行這個(gè)函數時(shí),會(huì )判斷變量是否true,是則返回。當第一次的定時(shí)器執行完函數最后會(huì )設定變量為false。那么下次判斷變量時(shí)則為false,函數會(huì )依次運行。目的在于在一定的時(shí)間內,保證多次函數的請求只執行最后一次調用。
這么看是不是有點(diǎn)看不懂?讓我們來(lái)看代碼:
//JS部分 function debounce(fun,wait=1000){//定時(shí)器方案 let timer = null;//先設定一個(gè)變量 return function(){ if(!timer){//如果timer為null就進(jìn)入 timer = setTimeout(function(){//然后把定時(shí)器賦值給這個(gè)變量 fun()//再寫(xiě)入需要執行的函數 timer = null//第一次的定時(shí)器執行完函數最后會(huì )設定變量為false,這里的 timer = null有兩個(gè)作用,1、開(kāi)啟下一次的入口,2、清除后面的定時(shí)器 }) } } } function testUname(){ console.log(Math.random()) } document.getElementById('myinput').addEventListener('input',debounce(testUname))
同樣的,節流函數也要解決this和arguments的問(wèn)題,改進(jìn)后如下:
//箭頭函數寫(xiě)法 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) } } }
到此為止,相信各位應該對函數節流有了一個(gè)比較詳細的了解,那函數節流一般用在什么情況之下呢?
到此這篇關(guān)于JS防抖節流函數的實(shí)現與使用場(chǎng)景的文章就介紹到這了,更多相關(guān)JS防抖節流函數內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站