JS防抖与节流
源码
前往Codepen在线体验。
介绍
防抖和节流是两个JS中的概念,它们被广泛应用于被频繁触发的事件中,如搜索框在输入时会弹出候选列表:如果每次输入都发送一个AJAX请求来获取数据,那么后台就要被刷爆了。所以,这里就引出了本文要介绍的概念。
防抖
在 x
秒内,无论调用多少次这个函数,它只会在最后一次调用的 x
秒后被真正执行。
在参考文章里举了这样一个例子:
一个小孩向妈妈要蛋糕,他的妈妈被弄烦了。所以她告诉小孩说,只有他能保持安静一个小时,才能得到蛋糕。中途任意要一次就重新开始计算。
我只是照着原文,摘了重点部分翻译过来放在上面。
节流
在x
秒内,无论调用多少次这个函数,它只会被执行一次。
在参考文章里举了这样一个例子:
还是那个小孩要蛋糕,但这次他的妈妈允许他无限制地要。尽管如此,不论他要多少次,只能在一小时内获取到一块蛋糕。如果他不再要了,自然也就不给了。
也是照着原文,摘了重点部分翻译过来放在上面。
实现
虽然这个概念是比较有用的,但是原生JS并没有给我们提供一个接口。无妨,借助setTimeout
可以轻松实现。
防抖
我们使用了JS里强大的闭包:
1 | function debounce(fn, delay) { |
可以看出,这个返回的函数无论被调用多少次,第一件事就是清除掉原有的定时器。如果到时间了,就执行函数fn
;如果被清除掉了,那就不执行。
节流
节流可以使用setTimeout
实现,也可以借助Date
判断调用时间。
使用setTimeout
贴代码:
1 | function throttle(fn, delay) { |
当定时器存在时,调用返回的函数不会做任何事。直到定时器被执行,timeout
被重新设置成了undefined
,才能进行下一轮操作。
使用Date
贴上代码:
1 | function throttleDate(fn, delay) { |
如果当前时间now
和上一次的时间prev
的差比预期时间delay
小,就什么也不做。否则,就执行函数fn
,并且重置上一次的时间prev
。
测试
写了这么多逻辑,没有测试自然是不合适的。这里就写个简单的网页来测试了:
1 | <button id="b1">Debounce: <span>0</span></button> |
加上点简单粗暴的样式:
1 | * { |
当然,还要把这些按钮的事件都绑定上:
1 | function bind(id, wrapper) { |
这里的bind
函数只是做了把指定id
的元素绑定上一个回调函数,执行时会使得它子节点中的span
元素自增。
同时,这个回调函数会被我们传入的wrapper
包装起来,也就是debounce
、throttle
或throttleDate
,并且延时都是500ms
,也就是0.5s
。
可以点击右侧的目录回到文章开头给源码的地方,到Codepen里实时预览最终效果。
应用
- 可以给按钮的
onclick
事件进行节流,用于防止用户频繁点击按钮。 - 可以给窗口的
resize
事件进行防抖,当最终重新调整大小后,再重新渲染页面。 - 可以给输入框的
keyup
,keydown
等事件进行防抖,当用户停止输入一段时间后弹出提示。 - 其实输入框的事件进行节流也可以,用于实时但不要太频繁地根据用户的输入弹出提示。
- 当NodeJS需要频繁更新文件到硬盘里的时候,进行防抖处理,这样只有在操作停止的一段时间后才会更新到硬盘里,有效减少IO操作。