防抖不在怕,来看就对了

701 阅读4分钟

前言

前端开发中会遇到一些频繁的事件触发,比如:window的scroll、resize;mousedown、mousemove,keyup、keydown等等,假如你对自己的代码不做什么的处理,你会发现页面卡顿、触发接口请求频繁等问题,本文将浅析函数防抖实现,一步一步逐渐揭开函数防抖的真面目💜

概念

理解防抖触发原理,根据不同使用场景合理使用

函数防抖(debounce)

当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间,不会执行

理解原理:
尽管触发事件,但是一定在事件触发 n 秒后才执行,如果在一个事件触发的 n 秒内又触发了这个事件,就以新的事件的时间为准,n秒后才执行,总之,就是要等触发完事件 n 秒内不再触发事件,才会执行!

实现

防抖

根据防抖原理,实现代码如下,之后的示例都将是常规使用:

# 箭头log函数
let count = 0
const log = () => {
    console.log(this)
    ++count
    console.log(count)
}
# 函数表达式
const log = function (evt) {
    console.log(this)
    console.log(evt)
    ++count
    console.log(count)
}
const debounce = function (fn, delay){
    let timeout = null
    return function () {
        if (timeout) {
            clearTimeout(timeout)
        }
        timeout = setTimeout(fn, delay)
    }
}

使用频繁click事件为例
常规使用: meContain.onclick = debounce(log, 1000)
react demo: ...onClick={debounce(log.bind(this), 1000)} 通过例子你会发现,1s之内频繁click,都会在最后输出一次log,验证了防抖原理

小伙伴们有没有发现此时的防抖函数仍存在缺陷

  • this指向和event 对象
  • 假如用户现在一直点击提交按钮的话,就会一直不发出请求,也得不到任何提示,对用户体验相当不好

this指向和event 对象

this指向

  • log函数中 console.log(this),不使用 debounce 函数的时候,this 的值为:undefined, 这是因为使用了箭头函数,此时需要onClick调用的时候bind(this), this指向react组件示例
  • 常规使用中console.log(this),不使用 debounce 函数的时候,this 的值为: <div id="mecontain"></div>

event 对象

  • 不使用 debouce 函数,会打印 ClickEvent 对象
  • debounce 函数中,却只会打印 undefined

解决以上问题,来更改我们的代码

const debounce = function (fn, delay){
    let timeout = null
    return function () {
        const context = this;
        const args = arguments;
        clearTimeout(timeout)
        timeout = setTimeout(() => {
            fn.apply(context, args)
        }, delay)
    }
}

交互优化体验

如果希望立刻执行函数一次,不用等到事件停止触发后才执行,然后等到停止触发 n 秒后,再可以重新触发执行
通过添加isImmeDiate来判断是否立刻执行

const debounce = function (fn, delay,isImmeDiate= false){
    let timeout = null
    return function () {
        const context = this;
        const args = arguments;
        if(timeout) clearTimeout(timeout)
        if(isImmeDiate) {
            # 判断是否已经执行过,不要重复执行
            let callNow = !timeout
            timeout = setTimeout(function(){
                timeout = null;
             }, delay)
            if(callNow)  result = fn.apply(context, args)
        } else {
            timeout = setTimeout(() => {
                fn.apply(context, args)
            }, delay)
        }
        return result
    }
}

如果要添加一个取消debounce开关,只需要添加一个cancle函数清除定时器timeout = null

const debounce = function (fn, delay,isImmeDiate= false){
    let timeout = null
    const debounced =  function () {
        const context = this;
        const args = arguments;
        if(timeout) clearTimeout(timeout)
        if(isImmeDiate) {
            # 判断是否已经执行过,不要重复执行
            # setTimeout也是一直在更新的
            let callNow = !timeout
            timeout = setTimeout(function(){
                timeout = null;
             }, delay)
            if(callNow)  result = fn.apply(context, args)
        } else {
            timeout = setTimeout(() => {
                fn.apply(context, args)
            }, delay)
        }
        return result
    }
    debounced.prototype.cancle = function() {
        clearTimeout(timeout)
        timeout = null
    }
    return debounced
}

到这里我们实现了一个防抖函数,但是小伙伴们有没有别的想法呢?

招聘广告-真香

随着电商的高速发展,技术&业务压力越来越大,人员补充成为了我们最大发展困境,规划再好,人员不足,也是枉然,来这里诚邀各路仙友一起聚首

这只是个示例:
1、掌握至少一种主流前端框架(react vue angular等),理解框架背后的原理和设计思想;
2、喜欢钻研前端技术,充满好奇心,具有良好的分析和解决问题能力;
3、关注前端前沿技术,具备较强的学习能力;
4、有较强的责任感、团队合作精神、逻辑思维能力和表达能力;

可以看出我们更在乎你的培养潜力和软素质能力。

加分项:
1、大前端技术社区活跃者、有自己的开源项目;
2、了解React Native、Flutter、小程序、Hybrid App等,有一定多端开发经验;
3、自我驱动,学习能力较强,性格活泼开朗;
4、有奇思妙想,善于把先进的思想和理念落地到项目中,并且有一定的技术推广能力;
5、对于mv*框架源码有一定的研究;