函数节流 & 函数去抖

函数去抖

浏览器中某些计算和处理要比其他的昂贵的多。例如,DOM操作比起非DOM交互需要更多的内存和cpu时间。连续尝试进行过多的DOM相关操作可能导致浏览器挂起甚至崩溃。尤其是IE中使用onresize事件处理程序的时候容易发生,当调整浏览器大小时,该事件便会频繁触发。在onresize事件处理程序内部如果尝试进行DOM操作,其高频率的更改可能会让浏览器崩溃。为了绕开这个问题,你可以使用定时器对该函数进行节流。

函数节流背后的基本思想是指,某些代码不可以在没有间断的情况连续重复进行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它将清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换成一个新的计时器。目的是只有在执行函数的请求停止了一段时间之后才执行。

以下是基本形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var processor = {
timeoutId: null,
// 实际进行处理的方法
performProcessing: function () {
// 实际执行的代码
},
process: function () {
clearTimeout(this.timeoutId)
var that = this
this.timeoutId = setTimeout(function () {
that.performProcessing()
}, 100)
}
}
// 尝试开始执行
processor.process()

这个模式可使用debounce()函数来简化,这个函数可以自动进行定时器的设置和清除,如下:

1
2
3
4
5
6
function debounce(method, context) {
clearTimeout(method.tId)
method.tId = setTimeout(function () {
method.call(context)
}, 100)
}

前面提过,节流在resize事件中是最常用的。例如,假设有一个<div/>元素需要保持它的高度始终等于宽度。那么实现这一功能的代码如下:

1
2
3
4
window.onresize = function () {
var div = document.getElementById('myDiv')
div.style.height = div.offsetWidth + ’px‘
}

这段代码里有两个问题可能会造成浏览器运行缓慢。首先,要计算offsetWidth属性,如果该元素有非常复杂的CSS样式,那么整个过程都会很复杂。其次,设置某个元素的高度需要对页面进行回流来令其更改生效。如果页面有很多元素同时应用了相当数量的CSS的话,这又需要很多计算。此时就可以用到throttle()函数。

1
2
3
4
5
6
7
function resizeDiv() {
var div = document.getElementById('myDiv')
div.style.height = div.offsetWidth + 'px'
}
window.onresize = function () {
debounce(resizeDiv)
}

主要应用场景:每次 resize/scroll 触发统计事件;文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)。

函数节流

在原生拖拽过程中(不用H5 drag),需要一路监听mousemove事件,然后重置dom的位置。如果不加以控制,每次移动1像素而触发的回调数量是大的惊人的。因此,我们需要降低触发回调的频率。比如让它500ms触发一次或者200ms。核心就是:不要让一个函数执行得太频繁,减少一些过快的调用来节流。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var throttle = function (action, delay){
var last = 0;
return function(){
var curr = +new Date();
if (curr - last > delay)
action.apply(this, arguments);
last = curr;
};
};

func () {
// ...
}

var fn = throttle(func, 100);

应用场景:DOM 元素的拖拽功能实现(mousemove)、计算鼠标移动的距离(mousemove)等

相关链接

JavaScript 函数节流和函数去抖应用场景辨析
underscore 函数去抖的实现
underscore 函数节流的实现