手写实现防抖

为什么

开发过程中会遇到一些的频繁的事件触发
增加服务器压力,导致页面卡顿,影响用户体验
提升性能,提高用户体验

  1. window 的 resize、scroll
  2. mousedown、mousemove
  3. keyup、keydown…

是什么

尽管触发事件,在触发事件的n秒后执行,如何在一个事件触发后的n秒内再次触发事件,以新的触发事件为准开始计时

(核心 : 清零)

怎么办

简单版 防抖函数

注意 有返回值 返回函数

//初始化定时器

let timerId = null;

  

//防抖函数

function debounce(fn,wait)

{

    return function(){

    //清除之前的计时器

    clearTimeout(timerId);

    //设置新的计时器

    timerId = setTimeout(fn,wait)

    }

}

bug

this指向

//调用的函数

function getUserAction() {

    console.log(this); // 应该是container元素,但是添加防抖函数后,this指向window

    container.innerHTML = count++;

};

解决

//初始化定时器

let timerId = null;

  

//防抖函数

function debounce(fn,wait)

{

    return function(){

        //获取当前的this指向

        let context = this;

    //清除之前的计时器

    clearTimeout(timerId);

    //设置新的计时器

    timerId = setTimeout(()=>{

        fn.apply(context);

    },wait)

    }

}

event对象

JavaScript 在事件处理函数中会提供事件对象 event,我们修改下 getUserAction 函数:

//调用的函数

function getUserAction(e) {

    console.log(e); // 应该是mouseevent事件对象,但是添加防抖函数后,undefined

    container.innerHTML = count++;

};

解决

//初始化定时器

let timerId = null;

  

//防抖函数

function debounce(fn,wait)

{

    return function(){

    //获取当前的this指向

    let context = this;

    //获取当前的参数

    let args = arguments;

    //清除之前的计时器

    clearTimeout(timerId);

    //设置新的计时器

    timerId = setTimeout(()=>{

        fn.apply(context,args);

    },wait)

    }

}

需求: 立刻执行

现在发现一触发事件,需要等待n秒后才执行
我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行。

//防抖函数

function debounce(fn,wait,immediate)

{

    //初始化定时器

  let timerId = null;

  let callNow

    return function(){

    //获取当前的this指向

    let context = this;

    //获取当前的参数

    let args = arguments;

    //清除之前的计时器

    clearTimeout(timerId);

    //判断是否立即执行

    if(immediate){

       // 如果已经执行过,不再执行

         callNow = !timerId;//每一次触发timerId都为null(会被清除),所以第一次为true,之后为false;但是这里

       // 设置新的计时器

       timerId = setTimeout(function(){

           timerId = null;

       }, wait)

       //没有执行过,立即执行

       if (callNow) fn.apply(context, args)

    }else{

        //不需要立即执行

        timerId = setTimeout(()=>{

            fn.apply(context,args);

        },wait)

    }

    }

}

注意:

  callNow = !timerId;
  //每一次触发timerId都为null(会被清除),所以第一次为true,之后为false;但是这里不太对

暂时这样理解
第一次,timer=null,callnow =true,立即执行了函数
第二次,在代码上方清除timer的时候,由于timer=一个定时器还没有执行成功,所以清除失败,但是callnow=!timer
这条语句执行了,callnow=false,然后又更新timer,不执行函数

需求:调用函数有返回值

此时注意一点,就是 getUserAction 函数可能是有返回值的,所以我们也要返回函数的执行结果,但是当 immediate 为 false 的时候,因为使用了 setTimeout ,我们将 func.apply(context, args) 的返回值赋给变量,最后再 return 的时候,值将会一直是 undefined,所以我们只在 immediate 为 true 的时候返回函数的执行结果。

//防抖函数

function debounce(fn,wait,immediate)

{

    //初始化定时器

  let timerId = null;

  let callNow

  //函数返回值

  let res;

    return function(){

    //获取当前的this指向

    let context = this;

    //获取当前的参数

    let args = arguments;

    //清除之前的计时器

    clearTimeout(timerId);

    //判断是否立即执行

    if(immediate){

       // 如果已经执行过,不再执行

         callNow = !timerId;//每一次触发timerId都为null(会被清除),所以第一次为true,之后为false;但是这里

       // 设置新的计时器

       timerId = setTimeout(function(){

           timerId = null;

       }, wait)

       //没有执行过,立即执行

       if (callNow) res=fn.apply(context, args)

    }else{

        //不需要立即执行

        timerId = setTimeout(()=>{

         fn.apply(context,args);

        },wait)

    }

  
  

    return res;

    }

  

}

需求:自由选择取消防抖

//防抖函数

function debounce(fn,wait,immediate)

{

    //初始化定时器

  let timerId = null;

  let callNow

  //函数返回值

  let res;

  var debounceMy= function(){

    //获取当前的this指向

    let context = this;

    //获取当前的参数

    let args = arguments;

    //清除之前的计时器

    clearTimeout(timerId);

    //判断是否立即执行

    if(immediate){

       // 如果已经执行过,不再执行

         callNow = !timerId;//每一次触发timerId都为null(会被清除),所以第一次为true,之后为false;但是这里

       // 设置新的计时器

       timerId = setTimeout(function(){

           timerId = null;

       }, wait)

       //没有执行过,立即执行

       if (callNow) res=fn.apply(context, args)

    }else{

        //不需要立即执行

        timerId = setTimeout(()=>{

         fn.apply(context,args);

        },wait)

    }

  
  

    return res;

    }

  

    debounceMy.cancel = function() {

        clearTimeout(timerId);

        console.log("取消了");

        timerId = null;

    };

  

    return debounceMy;

}

完整版

let count = 1;

let container = document.getElementById('container');

  

//防抖函数

function debounce(fn,wait,immediate)

{

    //初始化定时器

  let timerId = null;

  let callNow

  //函数返回值

  let res;

  var debounceMy= function(){

    //获取当前的this指向

    let context = this;

    //获取当前的参数

    let args = arguments;

    //清除之前的计时器

    clearTimeout(timerId);

    //判断是否立即执行

    if(immediate){

       // 如果已经执行过,不再执行

         callNow = !timerId;//每一次触发timerId都为null(会被清除),所以第一次为true,之后为false;但是这里

       // 设置新的计时器

       timerId = setTimeout(function(){

           timerId = null;

       }, wait)

       //没有执行过,立即执行

       if (callNow) res=fn.apply(context, args)

    }else{

        //不需要立即执行

        timerId = setTimeout(()=>{

         fn.apply(context,args);

        },wait)

    }

  
  

    return res;

    }

  

    debounceMy.cancel = function() {

        clearTimeout(timerId);

        console.log("取消了");

        timerId = null;

    };

  

    return debounceMy;

}

//调用的函数

function getUserAction(e) {

    console.log(e); // 应该是mouseevent事件对象,但是添加防抖函数后,undefined

    container.innerHTML = count++;

    return count;

};

  

let setUseAction = debounce(getUserAction, 1000,false);

  

container.onmousemove = setUseAction;

  

document.getElementById("button").addEventListener('click', function(){

    setUseAction.cancel();

})

  

// container.onmousemove = debounce(getUserAction,1000);

// container.onmousemove = getUserAction;

html

<!DOCTYPE html>

<html lang="zh-cmn-Hans">

  

<head>

    <meta charset="utf-8">

    <meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">

    <title>debounce</title>

    <style>

        #container{

            width: 100%; height: 200px; line-height: 200px; text-align: center; color: #fff; background-color: #444; font-size: 30px;

        }

    </style>

</head>

  

<body>

    <div id="container"></div>

    <button id="button">点击取消防抖</button>

    <script src="test.js"></script>

</body>

  

</html>
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值