平常工做中,常常遇到这样一个问题,在滚动事件中须要作个复杂计算或者实现一个按钮的防二次点击操做。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。毕竟那么多人在用,出现问题的几率会很小。缓存