前言
陆游有一首《冬夜读书示子聿》——“古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。”,其中的意思想必大家都能明白,在学习或工作中,不断的印证着这首诗的内涵。所以,又有了此篇小菊花文章。
详解
在前端开发中,我们经常会碰到一些会持续触发的时间,比如 输入框校验、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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。