小菊花课堂之JS的防抖与节流
前言
陆游有一首《冬夜读书示子聿》——“古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。”,其中的意思想必大家都能明白,在学习或工作中,不断的印证着这首诗的内涵。所以,又有了此篇小菊花文章。
详解
在前端开发中,我们经常会碰到一些会持续触发的时间,比如 输入框校验、resize、scroll、mousemove 等操作时,如果事件触发的频率无限制,会家中浏览器的负担,导致用户体验非常糟糕。
我们可以先看看持续触发过程中频繁执行函数是怎样的情况
<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div> <script> var num = 1; var content = document.getElementById('content'); function count() { content.innerHTML = num++; }; content.onmousemove = count; </script>
在上面代码中,div 元素绑定了 mousemove 事件,当鼠标在 div(灰色)区域中移动的时候会持续地去触发该事件导致频繁执行函数。
再看一个例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>没有防抖</title> <style type="text/css"></style> <script type="text/javascript"> window.onload = function () { //模拟ajax请求 function ajax(content) { console.log('ajax request ' + content) } let inputNormal = document.getElementById('normal'); inputNormal.addEventListener('keyup', function (e) { ajax(e.target.value) }) } </script> </head> <body> <div> 1.没有防抖的input: <input type="text" name="normal" id="normal"> </div> </body> </html>
在上面代码中,会监听键盘输入事件,只要按下键盘,就会触发这次模拟的ajax请求,不仅浪费了资源,而且在实际应用中,用户也是需要输入完整字符后,才请求。
防抖(debounce)
简单来说就是防止抖动,指触发事件在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
我们将上面的代码加入防抖优化一下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>加入防抖</title> <style type="text/css"></style> <script type="text/javascript"> window.onload = function () { //模拟ajax请求 function ajax(content) { console.log('ajax request ' + content) } function debounce(fun, delay) { return function (args) { //获取函数的作用域和变量 let that = this let _args = args //每次事件被触发,都会清除当前的timeer,然后重写设置超时调用 clearTimeout(fun.id) fun.id = setTimeout(function () { fun.call(that, _args) }, delay) } } let inputDebounce = document.getElementById('debounce') let debounceAjax = debounce(ajax, 500) inputDebounce.addEventListener('keyup', function (e) { debounceAjax(e.target.value) }) } </script> </head> <body> <div> 2.加入防抖后的输入: <input type="text" name="debounce" id="debounce"> </div> </body> </html>
上面代码加入防抖后,当持续在输入框里输入时,并不会发送请求,只有当在指定时间间隔内没有再输入时,才会发送请求。如果先停止输入,但是在指定间隔内又输入,会重新触发计时。
节流(throttle)
规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
我们同样在上面的需求上进行修改,加入节流函数。
//模拟ajax请求 function ajax(content) { console.log('ajax request ' + content) } function throttle(fun, delay) { let last, deferTimer return function (args) { let that = this; let _args = arguments; let now = +new Date(); if (last && now < last + delay) { clearTimeout(deferTimer); deferTimer = setTimeout(function () { last = now; fun.apply(that, _args); }, delay) } else { last = now; fun.apply(that, _args); } } } let throttleAjax = throttle(ajax, 1000) let inputThrottle = document.getElementById('throttle') inputThrottle.addEventListener('keyup', function (e) { throttleAjax(e.target.value) })
从上面代码可以看出,规定每一秒执行一次ajax请求,效果图也能比较清晰的反应出来。
小结
区别
函数防抖是某一段时间内只执行一次;而函数节流是间隔时间执行,不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。
在其他同学的文章中看到这样的解释:
防抖 — 如果有人进电梯(触发事件),那电梯将在10秒钟后出发(执行事件监听器),这时如果又有人进电梯了(在10秒内再次触发该事件),我们又得等10秒再出发(重新计时)。
节流 — 我们知道目前的一种说法是当 1 秒内连续播放 24 张以上的图片时,在人眼的视觉中就会形成一个连贯的动画,所以在电影的播放(以前是,现在不知道)中基本是以每秒 24 张的速度播放的,为什么不 100 张或更多是因为 24 张就可以满足人类视觉需求的时候,100 张就会显得很浪费资源。
这大概可以较为清晰的讲出这两者的区别吧。
原理
防抖是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,都会清除当前的 timer 然后重新设置超时调用,即重新计时。这样一来,只有最后一次操作能被触发。
节流是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。
文章来源
1.详谈js防抖和节流
2.轻松理解JS函数节流和函数防抖
3.函数防抖和节流
好啦,今天的小菊花课堂之JS的防抖与节流的内容就告一段落啦,感各位能耐心看到这里。
see u ~ again