函数的防抖和节流(手写+分析)

一、函数防抖

函数防抖的目的–对于频繁触发的某个操作,我们只识别一次,那么函数防抖的要素就来了:

  • 要被防抖的函数
  • “频繁”的界限
  • 识别的是频繁操作的第一次还是最后一次
所以可以得出防抖函数的大框架:
/*
 *   @params
 *      func:要被防抖,最后执行的函数
 *      wait:“频繁”的时间界限
 *		immediate:识别第一次,还是最后一次
 *   @return
 *      给要最终执行的func包一层匿名函数
 */
function debounce(func, wait, immediate) {
	return function anonymous() {};
}

这里包一层匿名函数,是为了形成闭包,将func, wait, immediate的值存储起来

接着,我们分析一下,防抖函数主要做的是什么呢?(以下用形参名来帮助理解)

  • 在事件触发的时候,我们要等wait这么长的时间,即延时执行func
  • 在wait期间,无第二次操作则立即执行func; 有第二次操作,即代表频繁操作了,那么重新等待wait时间
  • immediate先默认为false,即不考虑-----频繁点击时先执行第一次点击,后面点击无效果
得出防抖函数如下:
function debounce(func, wait, immediate = false) {
    let timer = null;
	return function anonymous() {
        clearTimeout(timer);
        timer = setTimeout(() => {
            func.call(this);//因为包了一层匿名函数,所以事件触发时,匿名函数的this是当前元素,而func不是,所以要将func的this也指向当前元素
        }, wait);
    };
}
大体上函数就是这样啦,但是别忘记还要分频繁操作以后执行第一次还是最后一次:
function debounce(func, wait, immediate) {
    let timer = null;
	return function anonymous() {
        //immediate => 想要执行频繁点击的第一次 && !timer 即第一次未设置定时器才为true
        let now = immediate && !timer;
        clearTimeout(timer);
        timer = setTimeout(() => {
            //每一次定时器能执行时手动将timer恢复成初始状态
            timer = null;
            //既然要执行第一次,那么就不要让wait时间之后再执行一次函数,这里用!immediate是因为最后一次操作的timer是不会被清除掉的
            !immediate ? func.call(this) : null;
        }, wait);

         //执行频繁点击第一次
         now ? func.call(this) : null;
    };
}
!!设个默认值,不传wait和immediate的时候,也方便使用啦~
<!-- body里 -->
<button id="submit">提交</button>
//防抖函数
function debounce(func, wait = 300, immediate = false) {
    let timer = null;
	return function anonymous(...params) {
        //immediate => 想要执行频繁点击的第一次 && !timer 即第一次未设置定时器才为true
        let now = immediate && !timer;
        clearTimeout(timer);
        timer = setTimeout(() => {
            //每一次定时器能执行时手动将timer恢复成初始状态
            timer = null;
            //既然要执行第一次,那么就不要让wait时间之后再执行一次函数,这里用!immediate是因为最后一次操作的timer是不会被清除掉的
            !immediate ? func.call(this,...params) : null;
        }, wait);

         //执行频繁点击第一次
         now ? func.call(this,...params) : null;
    };
}

//要执行的函数,也就是要赋给debounce里的func
function test() {
    console.log("ok");
}

//给事件绑定
submit.onclick = debounce(test, 500, true);

二、函数节流

函数节流的目的–在一段频繁操作中,可以触发多次,但是按照我们设定的频率,而之前说的防抖是只执行频繁操作里的一次。那么函数节流的要素是:

  • 要被节流的函数
  • 频率,也就是设定多少时间触发一次
所以可以得出函数节流的大框架:
function throttle(func, wait) {
    return function anonymous() { }
}

节流函数的主体思路:

  • 在频繁操作中每wait这么长的时间就执行一次操作
    • 需要记录上一次操作的时间
    • 需要记录当前时间以计算距离下一次执行的时间(用remaining表示)
  • 每两次操作间隔时间达到wait时长执行, 不到wait等待remaining这么长时间再执行
根据这个思路,来写函数吧:
function throttle(func, wait) {
    let timer = null,
        pervious = 0; // 用来记录上一次执行的时间,放在匿名函数的上级是为了保存这个变量的值
    return function anonymous() {
        let now = new Date(), //获取每次操作的当前时间
            remaining = wait - (now - pervious) //获取离执行下一次操作还剩多少时间

        if (remaining <= 0) {
            //间隔已经达到wait时间,执行func
            clearTimeout(timer); //既然要执行,那此时应该清掉现有的定时器
            timer = null;
            pervious = now; //获取当前操作的时间以作为下一次操作时间的上一次参照
            func.call(this);
        } else if (!timer) {
            //间隔没有达到wait时间,且没有设置过定时器(设过等到时间触发就好了)
            timer = setTimeout(() => {
                timer = null;
                pervious = new Date(); //每次定时器能够执行时获取当前时间
                func.call(this);
            }, remaining);
        }
    };
}
优化一下~~~
function throttle(func, wait = 300) {
    let timer = null,
        pervious = 0; // 用来记录上一次执行的时间,放在匿名函数的上级是为了保存这个变量的值
    return function anonymous(...params) {
        let now = new Date(), //获取每次操作的当前时间
            remaining = wait - (now - pervious) //获取离执行下一次操作还剩多少时间

        if (remaining <= 0) {
            //间隔已经达到wait时间,执行func
            clearTimeout(timer); //既然要执行,那此时应该清掉现有的定时器
            timer = null;
            pervious = now; //获取当前操作的时间以作为下一次操作时间的上一次参照
            func.call(this, ...params);
        } else if (!timer) {
            //间隔没有达到wait时间,且没有设置过定时器(设过等到时间触发就好了)
            timer = setTimeout(() => {
                timer = null;
                pervious = new Date(); //每次定时器能够执行时获取当前时间
                func.call(this, ...params);
            }, remaining);
        }
    };
}

三、总结

函数防抖是为了在频繁操作中只识别一次,适合点击提交表单这种;

函数节流是为了在频繁操作中只按照频率识别,适合页面滚动和输入过程中的模糊匹配等。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值