防抖和节流
在给DOM绑定事件时,有些事件我们是无法控制触发频率的。 如鼠标移动事件onmousemove
, 滚动滚动条事件onscroll
,窗口大小改变事件onresize
,瞬间的操作都会导致这些事件会被高频触发。 我们看个例子:
<div id="xxx" style="width: 100px; height: 100px; background-color: red;">1</div>
<script>
var xxx = document.getElementById('xxx');
xxx.onmousemove = test
function test() {
this.innerHTML = parseInt(this.innerHTML) + 1;
}
</script>
我们可以看到,上面的代码,当鼠标在div
之上移动的时候,会大量的触发事件,上面的代码比较简单,还比较好说。如果事件的回调函数较为复杂,就会导致响应跟不上触发,出现页面卡顿,假死现象。
针对此类快速连续触发和不可控的高频触发问题,debounce
和 throttle
给出了两种解决策略
防抖(debounce)
触发高频事件后n毫秒内函数只会执行一次,如果n毫秒内高频事件再次被触发,则重新等待n毫秒。也就是说,两个事件之间的等待时间可能是n毫秒,也可能是多个n毫秒。
基本思想是:首次运行时把定时器赋值给一个变量,第二次执行时,如果间隔没超过定时器设定的时间则会清除掉定时器,重新设定定时器,依次反复,当我们停止下来时,没有执行清除定时器,超过一定时间后触发回调函数。
<div id="xxx" style="width: 100px; height: 100px; background-color: red;">1</div>
<script>
var xxx = document.getElementById('xxx');
xxx.onmousemove = debounce(test, 1000);
function test() {
this.innerHTML = parseInt(this.innerHTML) + 1;
}
/**
* 一个防止抖动的函数(非立即执行版),会在第一次触发后等待一段事件再开始执行
* func 事件绑定的打算防抖的函数
* wait 防抖等待的时间
*/
function debounce(func, wait) {
// 定时器保存变量
var clock = null;
return function () {
if (clock) {
// 如果不是第一次触发,且上一次触发事件还没有执行完
// 就清掉定时器,在后面重新计时
clearTimeout(clock);
}
// 防止this指向发生改变
var obj = this;
// 设置定时器并开始计时
clock = setTimeout(function () {
// 事件触发后,且符合防抖策略,会在触发后等待一定时间执行
func.call(obj);
// 执行完事件,重置定时器保存变量
clock = null;
}, wait);
}
}
</script>
<div id="xxx" style="width: 100px; height: 100px; background-color: red;">1</div>
<script>
var xxx = document.getElementById('xxx');
xxx.onmousemove = debounce(test, 1000);
function test() {
this.innerHTML = parseInt(this.innerHTML) + 1;
}
// 立即执行版防抖函数
function debounce(func, wait) {
// 定时器保存变量
var clock = null;
return function () {
// 防止this指向发生改变
var obj = this;
if (clock) {
// 事件触发的时候,如果设置的有定时器,则清掉定时器,后面再重新设置
clearTimeout(clock);
}else{
// 如果是第一次或者在等待时间外,则直接执行事件处理函数
func.call(obj);
}
// 重新设置定时器
clock = setTimeout(function () {
clock = null;
}, wait);
}
}
</script>
节流(throttle)
指连续触发事件,但是在 n毫秒内只执行一次函数。也就是说,两个事件之间的等待时间是n毫秒。
<div id="xxx" style="width: 100px; height: 100px; background-color: red;">1</div>
<script>
var xxx = document.getElementById('xxx');
xxx.onmousemove = throttle(test, 1000);
function test() {
this.innerHTML = parseInt(this.innerHTML) + 1;
}
// 定时器版节流函数
function throttle(func, wait){
var clock = null;
return function () {
var obj = this;
// 如果首次触发或者已经延时完毕
if(clock == null){
clock = setTimeout(function () {
clock = null;
func.call(obj);
}, wait);
}
}
}
</script>