JavaScript 函数节流

  • throttle
  • debounce

为什么要函数节流

在下列场景中, 事件会被频繁触发, 从而导致频繁的执行DOM执行或资源加载, 导致UI停顿甚至浏览器崩溃。

  • Window对象的resize、scroll事件
  • 拖拽时元素时的mousemove事件
  • 按键的mousedown、keydown、keyup事件
  • 元素的onclick事件(加载资源)

例如, 以scroll事件进行解释:

window.onscroll = function(){
    lazyload();
};
function lazyload(){
    console.log("scroll执行了"+scrollnum);
}

本意只是想让鼠标滚动一次执行一次滚动函数, 但是window的onscroll函数并不是等 scroll结束之才会调用, 鼠标滚动或拖动滚动条, 就会不停的触发scroll事件, 如果处理的东西多, 低版本的IE也会陷入假死状态。

解决办法

debounce

当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。这种比较适合window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理;

去抖1
window.onscroll = function(){
  debounce(lazyload,window);
};
function debounce(method,context){
        clearTimeout(method.timeout);
        method.timeout = setTimeout(function(){
        method.call(context);
    },500);
}
function lazyload(){
    console.log("scroll执行了"+scrollnum);
}

利用定时器, 让函数执行延迟500毫秒, 在500毫秒内如果有函数又被调用则删除上一次调用, 这次调用500毫秒后执行, 如此反复。

去抖2

另一种节流方式, 是通过返回闭包的形式, 可以设置延迟时间, 两者运行的结果是一样, 但是在实际操作时设置延迟500毫秒时, 滚动过了一会儿才执行, 设置为delay为100的时候在视觉上就没有感觉延迟, 而且函数也只滚动了一次。

function debounce1(method,delay){
    var timer = null;
    return function(){
        var context = this, args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function(){
             method.apply(context,args);
         },delay);
    }
}

throttle

在上例代码中, 当鼠标一直滚动时, lazyload函数会被不断的延迟, 只有当鼠标停下来时才会被执行。

但这在有些需要及时显示的情况下, 就显得不那么友好了(对于实现keyup事件的提示也没有意义了), 所以可以为函数添加一个参数作为固定间隔, 到了这个时间间隔就必须执行, 这个时候就引入了节流:

节流: 如果将水龙头拧紧直到水是以水滴的形式流出, 此时会发现每隔一段时间, 就会有一滴水流出, 也就是说会预先设定一个执行周期, 当调用动作的时刻大于等于执行周期则执行该动作, 然后进入下一个新周期。
代码如下:

function throttle2(method, delay, time) {
      var timeout,startTime = new Date();
      return function() {
            var context = this, args = arguments,
            var curTime = new Date();
            clearTimeout(timeout);
            // 若达到了触发的时间间隔,则立即触发handler
            if (curTime - startTime >= time) {
                    method.apply(context, args);
                    startTime = curTime;
             // 若没有达到触发的时间间隔,则重新设定定时器
            } else {
                timeout = setTimeout(method, delay);
            }
};

在这个函数中, 当一次时间较长的时候还是会执行两次, 而不是等滚动停止之后再执行。 达到了想要的效果, 既没有频繁的执行也没有最后执行。

你可能感兴趣的:(JavaScript 函数节流)