浏览器中某些计算和处理要比其他的昂贵的多。例如,DOM操作比起非DOM交互需要更多的内存和CPU时间。连续尝试进行过多的DOM相关操作可能会导致 浏览器挂起,有时候甚至会崩溃。尤其在IE中使用onresize事件处理程序的时候容易发生,当调整浏览器大小的时候,该事件连续触发。在 onresize事件处理程序内部如果尝试进行DOM操作,其高频率的更改可能会让浏览器崩溃。
函数节流背后的基本思想是,某些代码不可以在没有间断的情况连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用 该函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其 替换为一个新的定时器。目的是只有在执行函数的请求停止了一段时间之后才执行。
1
2
3
4
5
6
|
function throttle ( method , context ){ clearTimeout ( method.tId ); method.tId = setTimeout ( function () { method.call ( context ); } , 100); } |
应用举例:
假设有一个<div/>元素需要保持它的高度始终等同于宽度,可作如下编码:
1
2
3
4
5
6
7
|
function resizeDiv(){ var div = document.getElementById( "mydiv" ); div.style.height = div.offsetWidth + "px" ; } window.onresize = function (){ throttle(resizeDiv); } |
这里,调整大小的功能被放入了一个叫做resizeDiv的单独函数中,然后onresize事件处理程序调用throttle()并传入 resizeDiv函数,而不是直接调用resizeDiv()。多数情况下,用户是感觉不到变化的,虽然给浏览器节省的计算可能非常大。
下面是其他网友的补充
今天主要写写我们平时工作中需要的函数节流。可能有的朋友对函数节流没有意识。其实,在工作中,很多场景都需要我们进行js的节流。最常见的是屏幕伸缩resize,以及touchmove或者scroll等事件的时候。大家不知道有没有看我之前写的文章!jquery判断页面滚动条上滚下滚,touchmove的滑动方向,大家在使用这些例子的时候,会发现页面不停的触发touchmove或者scroll因为这里没有关系到页面的重绘,因此,我在这里没有使用javascript函数节流。但是,当我们使用window.onresize的时候,也会不停的触发resize事件!这里就会关系到页面的重新绘制问题了。因此,在window的resize的时候,我们推荐大家使用函数节流的方式!
javascript函数节流简介
假如你对我上面一大坨文字感到头大,没关系,我在这里简单举例说明一下函数节流吧!例如当我们使用
1
2
3
|
$(window).resize( function (){ console.log( "haorooms window resize" ); }) |
会发现:
这里会输出好多次。我们简单的缩小一下窗口,就会不停的触发!
这样在div很多的时候,页面不停重绘,要是遇到版本比较低的IE等,很可能会出现浏览器崩溃的现象!为了避免这种情况,我们可以用函数节流的方式。基本的思想是:第一次调用函数的时候,我们创建一个定时器,在指定时间间隔之后运行代码,第二次调用的时候,会清楚前一个定时器,并重新设置一个。如果前一个定时器已经执行过了,那么这个操作就没有有意了,如果定时器尚未执行,就会将其替换为一个新的定时器。目的是在执行函数停止了一段时间之后再执行。
用对象的方式可以如下写:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var haoroomstest={ timeoutId: null , performProcessing: function (){ console.log( "resize" ); }, process: function (){ clearTimeout( this .timeoutId); var that= this ; this .timeoutId=setTimeout( function (){ that.performProcessing(); },500) } } |
这样之后,我们再用:
$(window).resize(function(){ haoroomstest.process(); })
这样就会减少请求,减少dom重绘,达到节流的目的!
函数节流其他方式
除了我们运用对象的方式,网上及资料中也介绍了关于函数节流的其他方法和方式,我下面简单介绍几种!
函数方式一
1
2
3
4
5
6
|
function throttle(method,context){ clearTimeout(method.tId); method.tId=setTimeout( function (){ method.call(context); },100); } |
我们如下使用
1
2
3
4
5
6
7
|
function resizeDIv(){ console.log( "haorooms" ) } $(window).resize( function (){ throttle(resizeDIv) }) |
和上面对象实现了同样的效果!
函数方式二
网上还有一种比较流行的节流方式,我在这里写一下!
1
2
3
4
5
6
7
8
9
10
|
function throttle(method,delay){ var timer= null ; return function (){ var context= this , args=arguments; clearTimeout(timer); timer=setTimeout( function (){ method.apply(context,args); },delay); } } |
然后可以这么写:
1
2
3
4
5
|
function resizeDIv(){ console.log( "haorooms" ) } window.onresize=throttle(resizeDIv,500); |
新需求
我们在做模糊搜索智能联想提示的时候,会在input上面绑定keyup事件。但是我又不想触发的那么频繁,用上面的方式就会有问题。因此,在上面的函数基础上稍加改动,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function throttle(method,delay,duration){ var timer= null , begin= new Date(); return function (){ var context= this , args=arguments, current= new Date();; clearTimeout(timer); if (current-begin>=duration){ method.apply(context,args); begin=current; } else { timer=setTimeout( function (){ method.apply(context,args); },delay); } } } |
这样触发就不会有之前那么频繁了!