【DG】手写实现防抖&节流

使用目的:

解决当函数在某些场景下被无限制的频繁调用,会增长浏览器的负担,会形成卡顿的现象浏览器

使用场景:

鼠标滚动、键盘输入操做、窗口resize等等闭包

原理:

防抖:事件持续触发时,若是间隔时间小于预设的时间,不会执行函数,同时以最后触发事件的时间为准从新计时。总之,必定要在事件触发完的预设时间内没有触发过,才会执行函数app

手写+逐步完善函数

第一个实现版本(注意执行上下文)

function _debounce(func, wait){
    // 定义一个计时器
    var timer
    return function(){
        // 拿到当前的执行上下文
        var context = this
        clearTimeout(timer)
        timer = setTimeout(function(){
            // 由于setTimeout回调方法中this永远是window,因此~
            func.apply(context)
        },wait)
    }
    
}

以上实现方式有一个不足,就是当事件处理函数传入参数(event对象)时,没有作到获取,修改一下:优化

第二个实现版本(注意处理传参)

function _debounce(func, wait){
    var timer
    return function(){
        var context = this
        // 经过arguments拿到调用时所传入的参数
        var arg = arguments
        clearTimeout(timer)
        timer = setTimeout(function(){
            // 执行时传入参数
            func.apply(context, arg)
        },wait)
    }
    
}

此时还有一个问题,最开始函数必需要在事件初次触发后,等待wait以后才会执行,咱们想要用一次当即执行来兜底初次触发:this

第三个实现版本(处理初始当即执行)

function _debounce(func, wait, immediate){
    var timer
    return function(){
        var context = this
        var arg = arguments
        if(timer) clearTimeout(timer)
        if(immediate){
            /** timer为true,callNow为false期间
             *  都表明不是初次触发
             *  timer初始布尔值为false
             *  初次执行以后timer在wait时间内一直为true
            **/
            var callNow = !timer
            // 这一句在执行完以前 timer都为true
            timer = setTimeout(function(){
                timer = null
            },wait)
            if(callNow) func.apply(context,arg)
        }else{
            timer = setTimeout(function(){
                func.apply(context, arg)
            },wait)
        }
    }  
}
// 中心思想就是有一个标识,用来标记当前是否为初次触发,用以判断是否须要执行,而在执行后wait时间后将标识变为初始状态

**特别版本:防抖函数有可能会形成一直不触发的现象code

function throttle(fn, delay){
  /*last为上一次触发回调的时间, timer是定时器*/
  var last = 0, timer = null
  
  return function () { 
    let context = this
    let args = arguments
    let now = +new Date()
    
     /*判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值*/
    if (now - last < delay) {
    /* 若是时间间隔小于咱们设定的时间间隔阈值,
     * 则为本次触发操做设立一个新的定时器
     * 其实这里就是本来正常的防抖逻辑
     */
       clearTimeout(timer)
       timer = setTimeout(function () {
          last = now
          fn.apply(context, args)
        }, delay)
    } else {
        /* 若是时间间隔超出了咱们设定的时间间隔阈值,那就不等了,不管如何要反馈给用户一次响应*/
        last = now
        fn.apply(context, args)
    }
  }
}

还能够有一些优化,好比有返回值时等等,暂时到这里 最好能记牢对象


节流:事件持续触发期间,每间隔预设的时间才会执行一次事件

手写+逐步完善(定时器 or 时间戳)
一、时间戳版本rem

function _throttle(func, wait){
    var timer = 0
    return function(){
        var arg = arguments
        var now = new Date()
        if(now - timer >= wait){
            func.apply(this,arg)
            timer = now
        }
    }
}
/** 这里懵逼了,理解一下应该是将throttle执行后的结果,
 *  也就是闭包函数赋值给了container的mousemove
 *  以后每次mousemove执行的都是闭包函数
**/
container.onmousemove = throttle(xxx,1000)

二、定时器版本

function _throttle(func,wait){
    var timer
    return function(){
        var arg = arguments
        if(!timer){
            timer = setTimeout(function(){
                timer = null
                func.apply(this,arg)
            },wait)
        }
    }
}

以上两种实现方式,时间戳版本会当即执行,但不会有最后一一次执行;定时器版本不会当即执行,但会有最后一次操做执行兜底
最好是将这两种状况合二为一,更加优秀

三、取长补短版本:

function throttle(func, wait) {
    var timeout, context, args, result;
    var previous = 0;

    var later = function() {
        previous = +new Date();
        timeout = null;
        func.apply(context, args)
    };

    var throttled = function() {
        var now = +new Date();
        //下次触发 func 剩余的时间
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
         // 若是没有剩余的时间了或者你改了系统时间
        if (remaining <= 0 || remaining > wait) {
            // 对于时间戳计算,要执行的时候若是还有计时器捣乱 要清掉
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
        } else if (!timeout) {
            // 若是时间戳尚未到执行,定时器到了(没有定时器表明要执行了),那就在剩余时间后执行,兜最后一次
            timeout = setTimeout(later, remaining);
        }
    };
    return throttled;
}
// 这个难 暂时先理解吧
相关文章
相关标签/搜索