防抖
你是否在日常开发中遇到一个问题,在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。
这些需求都可以通过函数防抖动来实现。尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。
PS:防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的时间小于设定的间隔时间,防抖的情况下只会调用一次,而节流的情况会每隔一定时间(设定的延迟时间)调用函数。
我们先来看一个袖珍版的防抖理解一下防抖的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
const debonce = (func, delay = 100, ...args) => { let timer = null return function () { if (timer) clearTimeout(timer) timer = setTimeout(() => { func.apply(this, args) }, delay) } }
|
这个简单版的防抖有个缺陷,它只能在延迟过后调用。我们有时候需要立即调用回调函数,比如点击获取验证码就可以看做是延迟时间比较久的立即执行的防抖函数,现在我们试着做个立即执行版。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
const debonce = (func, delay = 100, ...args) => { let timer = null return function () { if (timer) { clearTimeout(timer) } else { func.apply(this, args) } timer = setTimeout(() => { timer = null }, delay) } }
|
我们也可以将两个版本合并起来,多传入一个参数来判断其是哪一种防抖函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
const debonce = (func, delay = 100, immediate = true, ...args) => { let timer, context later = () => setTimeout(() => { clearTimeout(timer) timer = null if (!immediate) { func.apply(context, args) } }, delay) return function () { if (!timer) { if (immediate) { func.apply(this, args) } else { context = this } timer = later() } else { clearTimeout(timer) timer = later() } } }
|
整体函数实现的不难,总结一下。
- 对于按钮防点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,你每次点击我都重新计时。一旦你点累了,定时器时间到,定时器重置为
null
,就可以再次点击了。 - 对于延时执行函数来说的实现:清除定时器 ID,如果是延迟调用就调用函数
节流
防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
const throttle = (func, duration, ...args) => { let previous = 0 return function () { let current = new Date() if (current - previous >= duration) { func.apply(this, args) previous = current } } }
|
最后更新时间:
当前页访问次数 10