平常工做中,常常遇到這樣一個問題,在滾動事件中須要作個複雜計算或者實現一個按鈕的防二次點擊操做。javascript
const debounce = (fn, wait = 800) => {
let timer = null
return function() {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
// 用 .apply 保證 sayHi 裏面的 this 指向事件的調用者,不然指向 window
fn.apply(this, arguments)
}, wait);
}
}
function sayHi() {
console.log('防抖成功')
}
let inp = document.querySelector('#input')
inp.addEventListener('input', debounce(sayHi))
複製代碼
const debounce = (fn, wait = 800, immediate = true) => {
let timer, context, args
// 延遲執行函數
const later = () => setTimeout(() => {
// 延遲函數執行完畢,清空緩存的定時器序號
timer = null
if (!immediate) {
fn.apply(context, args)
context = args = null
}
}, wait);
// 返回的函數是每次實際調用的函數
return function(...params) {
// 若是沒有建立延遲函數 later, 就建立一個
if (!timer) {
timer = later()
// 如果當即執行函數,則調用函數
// 不然緩存參數和調用上下文
if (immediate) {
fn.apply(this, params)
} else {
context = this
args = params
}
// 若是已經有延遲執行函數 (later), 調用的時候清除原來的並從新設定一個
} else {
clearTimeout(timer)
timer = later()
}
}
}
function sayHi() {
console.log(this);
console.log('防抖成功');
}
let inp = document.querySelector('#input')
inp.addEventListener('input', debounce(sayHi))
複製代碼
const throttle = (fn, wait = 400) => {
let canRun = true
return function (...args) {
if (!canRun) return
canRun = false
setTimeout(() => {
fn.apply(this, args)
// 最後在 setTimeout 執行完畢後再把標記設置爲 true (關鍵)表示能夠執行下一次循環了。當定時器沒有執行的時候標記永遠是 false,在開頭被 return 掉
canRun = true
}, wait);
}
}
function sayHi() {
console.log(this);
console.log('節流成功');
}
let inp = document.querySelector('#input')
inp.addEventListener('input', throttle(sayHi))
複製代碼
總結(使用場景):java
函數防抖:npm
函數節流:瀏覽器
PS: 生產環境中,建議用 lodash 中的 debounce 和 throttle。畢竟那麼多人在用,出現問題的機率會很小。緩存