JS:手搓一份防抖和节流函数

前言:面临秋招,防抖节流是重要的考点,是前端用来优化高频事件的方法,写个文章梳理一下。

  • 防抖:规定时间内只执行一次。 (重新计算时间)

  • 逻辑:当事件被触发时,不会立即执行对应的处理函数,而是等待一段预设的时间。

  • 场景:掘金登录的时候,用户🦊一直狂摁登录按键,会导致多次登录请求,消耗性能。 因此采用防抖的方式,每次摁登录按键,清除之前的等待时间,再等一段时间,这样让狐狸先生最后一次的点击登录按键成功。

  • 项目中使用,引入lodash库:

js 复制代码
    import _ from "lodash"
    
    <button onclick={_.debounce(()=>navigate("./home"),500)}/>
    //debounce()传入两个参数一个是函数体一个是number

注意到debounce中需要传入两个参数 ,一个是函数体 fn,一个是时间delay,表示需要等待delay时间后执行fn,定下大体逻辑。

js 复制代码
const debounce = (fn,delay)=>{
    //直接采用定时器 , delay时间后调用fn
        setTimeout(() => {
        fn()
    }, delay)
}

显然是有问题的,因为onclick需要接收一个函数,再调用它,改一下返回一个函数体,注意这里是函数体,如果是匿名函数则this在 V8 非严格模式 会指向window,node环境指向undefined

js 复制代码
const debounce = (fn, delay) => {
    return function () {
        setTimeout(() => {
            fn()
        }, delay)
    }
}

还有问题,这里的定时器只会执行一次,明显不满足重新计算时间的要求,稍微改一下,创建一个timer,返回一个函数体,因为返回函数体 中用上了timer ,会形成一个闭包

js 复制代码
const debounce = (fn, delay) => {
    let timer = null;
    return function () {
        //这里有个闭包 timer 
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn()
        }, delay)
    }
}

如果这个放到button里面会是什么样呢?

js 复制代码
//<button onclick={debounce(()=>navigate("./home"),500)}/>
<button onclick={
    function () {
        //这里有个闭包 timer 每一次点击时的timer都是同一个哦
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            navigate("./home")
        }, 500)
    }
}/>

现在每次点击button,都会把上次的定时器清除,看起来大功告成。(你看看你后面,你再看看你后面,你再看看你后面......)

注意平常我们调用onclick中的会传入一个e(事件参数)以及其它参数,这里没法传事件参数,再改一点点代码。

js中的 "... " 有两种语义,一种是收集,一种是解构,所以我用"... "来收集 传入的参数再用"... "将传入参数解构出来

js 复制代码
const debounce = (fn, delay) => {
    let timer = null;
    return function (...args) {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn(...args)
        }, delay)
    }
}

这样就完成了吗,这里的fn 函数是独立调用的,如果fn中有用this,this在 V8 非严格模式下会指向window,node环境指向undefined,这明显有bug,稍微再改一点,给 this 掰弯来。

js 复制代码
const debounce = (fn, delay) => {
    let timer = null;
    return function (...args) {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(this, args)//apply接受的是数组
            //或者
            fn.call(this,...args)//call能接受独立的值
        }, delay)
    }
}

检查一遍,写回button里看看

js 复制代码
// <button onclick={debounce(
//   (e)=>{ 
//        console.log(e)
//        }
//,500)}/>

 <button onclick={
     function (e) {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout((e) => {
         //   fn.apply(this, [e])//apply接受的是数组
          console.log(e)
        }, 500)
    }}/>

好像没问题欸嘿,面试一定写得出来。

结果

js 复制代码
const debounce = (fn, delay) => {
    let timer = null;
    return function (...args) {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, delay)
    }
}
相关推荐
bug_kada4 小时前
Js 的事件循环(Event Loop)机制以及面试题讲解
前端·javascript
bug_kada4 小时前
深入理解 JavaScript 可选链操作符
前端·javascript
LuckySusu19 小时前
【js篇】JavaScript 对象创建的 6 种方式:从基础到高级
前端·javascript
LuckySusu19 小时前
【js篇】async/await 的五大核心优势:让异步代码像同步一样清晰
前端·javascript
艾雅法拉拉19 小时前
JS知识点回顾(1)
前端·javascript·面试
LuckySusu19 小时前
【js篇】Promise 解决了什么问题?—— 彻底告别“回调地狱”
前端·javascript
游荡de蝌蚪20 小时前
快速打造Vue后台管理系统
前端·javascript·vue.js
June_liu1 天前
列太多vxe-table自动启用横向虚拟滚动引起的bug
前端·javascript
云枫晖1 天前
手写Promise-then的基础实现
前端·javascript