防抖函数
先总结下对防抖函数的理解:

防抖函数就是防止用户进行一些点击事件或者输入文字时,对应绑定的事件发生多次

比如我在点击后发送ajax请求,但是我联系按了两次,然后请求就发送了两次

或者在输入框中打字时候,还没有结束打字就已经发送了请求

(当然有些搜索框正需要这样的功能,但是也是需要限制,肯定也得等完整的字符输入才显示推荐)

防抖函数具体实现:

就是用一个函数把原本要执行的事件函数包裹起来,利用setTimeout函数来规定执行的时机,同时把原本绑定的事件函数获得的参数全部给这个函数,因为会涉及到this指向和参数问题,具体看代码:

let submit = document.getElementById('debounceSubmit')
submit.addEventListener("click", debounce(sub), false)

function sub(e){
    console.log(1);
}
//防抖
function debounce(fn) {
    console.log(1)
    let t = null //只会执行一次
    return function (){
        if(t){
            clearTimeout(t)
        }
        t = setTimeout(()=>{
            // console.log(temp);  //可以获取
            // console.log(arguments[0]) //undefined
            fn.apply(this,arguments)
            //在这个回调函数里面的argument是这个回调函数的参数,因为没有参数所以undefined,可以通过外面的函数赋值来进行访问
            //也可以改变成箭头函数,箭头函数的this是指向定义函数的那一层的,所以访问到的arguments是上一层函数的arguments
        },1000)

    }
}
代码要点:
  1. debounce函数只会执行一次,submit真正绑定的函数是debounce里面返回的函数,就是绑定+执行,后面就直接俄执行,所以t不会被重复定义成null
  2. 如果t不存在那么设置一个倒计时,时间到了执行传进来的函数(在绑定的时候传进来)
  3. 这里通过apply使得submit执行时,拥有这个返回的函数的作用域,可以说这个点击事件绑定了submit(因为原本这个事件就是直接绑定submit,现在只是通过apply把作用域给到了submit
  4. 但是有了作用域还不够,原本的事件参数还没有给到submit,所以,因为apply的第二个的参数是一个数组,所以直接传arguments。
  5. 但是这个arguments,原本应该是setTimeout函数的参数,但是它是没有参数的,所以在这个倒计时函数里打印是undefined(原本普通函数时候的结果,this此时也是指向的是window,看下面的代码)
  6. 又因为我们用的是箭头函数,箭头函数的this指向上一层,即定义他的作用域,所以arguments就是事件绑定的参数
  7. 这样就成功把该给这个事件函数的都给到了
  8. 做这些apply,arguments的意义,我是这么理解的就是为了不让我这个防抖函数的包装影响到原本这个自定义事件函数,原本有什么,现在就该有什么
t = setTimeout(function (){
	console.log(this)  //window
    console.log(arguments[0]) //undefined
    })

t = setTimeout(()=>{
	console.log(this)  //<input>
    console.log(arguments) // PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure
    })
改良版本(第一次点击会直接触发)
let submit = document.getElementById('debounceSubmit')
submit.addEventListener("click", debounce(sub,1000,true), false)

function sub(e){
    console.log(1);
}
//防抖
function debounce(fn,timer,triggerNow) {
    let t = null //只会执行一次
    return function (){
        if(t){
            clearTimeout(t)
        }
        if(triggerNow){
            let firstClick = !t
            if(firstClick){
                fn.apply(this,arguments)
            }
            t= setTimeout(() =>{
                t = null
            },timer)
        }
    }
}

基础不好,再理一遍逻辑

  1. 首先执行debounce,t=null,返回一个函数,进行绑定,并且进行第一次调用
  2. 进入绑定的函数,trigglerNow=true这里的含义就是第一次调用不需要倒计时
  3. 因为t=null,所以是第一次点击,执行submit,然后倒计时1s将t变成null
  4. 这里的倒计时可以这么理解,就是我连续1s没有按,那么下一次点击又会被视作第一次点击
  5. 开头的判断就是阻止这1s内额外的点击,如果点击了,那么就重来

最后突然想到,关于t为什么一直会存在,外面用来包裹的函数虽然已经结束,或者说销毁,但是因为闭包的存在,里面的函数依旧可以引用外面的函数的变量

欢迎指正~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐