Open LeoWangJ opened 5 years ago
在功能頻繁觸發的情況下,當只有在功能觸發的時間間隔超過我們所指定的時間,才會觸發此功能
簡單來說就是某個函數在某個指定的時間內不管觸發多少次, 都只會執行最後一次的觸發。
舉例來說: 我們設了一個五秒的指定時間, 則在這五秒內每觸發了一次函式,就會重新計算這五秒, 直到最後一次五秒內都沒有重新觸發函式時就會執行任務。
常用場景:
有時候我們只是想知道最後的結果,但上述這些方法在發生變動時會頻繁觸發,因此我們需要使用防抖來節省資源。
例如註冊帳號時,我們需要去檢驗此帳號是否已經被註冊過了,但需求方希望可以在使用者打字時就可以監測,而不是輸入框失去焦點時才判斷。
這時我們可能會這樣寫
$(".register").on("input", function(){ // 請求後端來判斷是否使用者已經有被註冊,但這個函式不是我們主要探討的部分 handleRegisterName(this.value) })
當今天我想要將帳號名稱取為leowang時,會發現我們每次輸入文字都會觸發handleRegisterName這個方法,這樣就多請求許多次不必要的資源。
這時我們不就可以使用防抖來解決這個問題了嗎? 那防抖怎麼寫呢? 你想到了上面的那行 "當只有在功能觸發的時間間隔超過我們所指定的時間,才會觸發此功能"。 這時需要setTimeout來判斷時間是否有超過間隔以及定義兩個參數,分別是要觸發的函式以及指定的時間
// 防抖函式 function debounce(fn , wait){ var timeout return function(){ if(timeout) clearTimeout(timeout) timeout = setTimeout(fn, wait) } } $("#register").on("input", debounce(function(){ console.log(this.value) handleRegisterName(this.value) }, 300))
完成後發現了this.value 怎麼是undefined? 檢查一下發現this被指向了window, 這是因為是setTimeout呼叫了這個回呼函式,除了這個問題之外,我們還想要傳遞一些參數進去函式中,例如event。所以我們必須調整一下debounce函式。
透過apply方式綁定上下文情境以及使用arguments取得函數參數。
function debounce(fn, wait) { var timeout, args return function(){ args = arguments if(timeout) clearTimeout(timeout) timeout = setTimeout(() => { fn.apply(this, args) }, wait) } }
這時需求方又說希望一輸入文字時,就先執行一次。所以我們就當時間到時將timeout 設成null,使得可以直接執行fn方法
function debounce(fn, wait, imd) { var timeout,args return function(){ args = arguments if(timeout) clearTimeout(timeout) if(imd) { var callNow = !timeout; timeout = setTimeout(function(){ timeout = null; }, wait) if (callNow) fn.apply(this, args) } else { timeout = setTimeout(() => { fn.apply(this, args) }, wait) } } }
但上面還有一個問題, 就是我們的fn假如有回傳值時, 上面那一個版本是無法接收的, 所以我們必須修正這個問題。
但要注意的是, 回傳值版本只提供給立即執行, 因為在非立即執行中fn是在setTimeout內, 導致我們的return的值永遠都是undefined 。
var debounce = function(fn, wait, immediate){ var timeout,result,args return fuction() { args = arguments if(timeout) clearTimeout(timeout) if(immediate) { var callNow = !timeout timeout = setTimeout(function(){ timeout = null; }, wait) if(callNow) result = fn.apply(this, args) } else { timeout = setTimeout(() => { fn.apply(this, args) }, wait) } return result } }
从搜索系统来聊聊防抖和节流 JavaScript专题之跟着underscore学防抖
防抖(debounce)
簡單來說就是某個函數在某個指定的時間內不管觸發多少次, 都只會執行最後一次的觸發。
舉例來說: 我們設了一個五秒的指定時間, 則在這五秒內每觸發了一次函式,就會重新計算這五秒, 直到最後一次五秒內都沒有重新觸發函式時就會執行任務。
常用場景:
有時候我們只是想知道最後的結果,但上述這些方法在發生變動時會頻繁觸發,因此我們需要使用防抖來節省資源。
例如註冊帳號時,我們需要去檢驗此帳號是否已經被註冊過了,但需求方希望可以在使用者打字時就可以監測,而不是輸入框失去焦點時才判斷。
這時我們可能會這樣寫
當今天我想要將帳號名稱取為leowang時,會發現我們每次輸入文字都會觸發handleRegisterName這個方法,這樣就多請求許多次不必要的資源。
這時我們不就可以使用防抖來解決這個問題了嗎? 那防抖怎麼寫呢? 你想到了上面的那行 "當只有在功能觸發的時間間隔超過我們所指定的時間,才會觸發此功能"。 這時需要setTimeout來判斷時間是否有超過間隔以及定義兩個參數,分別是要觸發的函式以及指定的時間
修正this問題與傳遞參數
完成後發現了this.value 怎麼是undefined? 檢查一下發現this被指向了window, 這是因為是setTimeout呼叫了這個回呼函式,除了這個問題之外,我們還想要傳遞一些參數進去函式中,例如event。所以我們必須調整一下debounce函式。
透過apply方式綁定上下文情境以及使用arguments取得函數參數。
立即執行功能
這時需求方又說希望一輸入文字時,就先執行一次。所以我們就當時間到時將timeout 設成null,使得可以直接執行fn方法
返回值
但上面還有一個問題, 就是我們的fn假如有回傳值時, 上面那一個版本是無法接收的, 所以我們必須修正這個問題。
但要注意的是, 回傳值版本只提供給立即執行, 因為在非立即執行中fn是在setTimeout內, 導致我們的return的值永遠都是undefined 。
參考
从搜索系统来聊聊防抖和节流 JavaScript专题之跟着underscore学防抖