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)
    }
}
相关推荐
云浪6 小时前
Go 并发实战:从零实现一个高并发安全的任务调度器
javascript·后端·go
OpsEye14 小时前
大模型账单对账难:不同厂商 Token 统计口径差异怎么处理
javascript·ai编程
全栈项目管理程序猿14 小时前
ArcGIS JS 基础教程(32):体元系列 - VoxelSlice 体元切片
前端·javascript
Hilaku17 小时前
AI 流式输出都用 SSE,什么时候必须换 WebSocket?
前端·javascript·程序员
奇牙coding17 小时前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai
不简说17 小时前
AI 帮你画模板、点一下就云打印:sv-print 这三个月都干了啥
前端·javascript·人工智能
大明二代18 小时前
从零构建 Mastra RAG MVP:DeepSeek、SiliconFlow 与 PostgreSQL/pgvector 实战
javascript·数据库·人工智能
结局无敌18 小时前
React Native for OpenHarmony 实战:三方库 react-native-paper 的鸿蒙化适配指南
javascript·人工智能·深度学习·react native·react.js·ecmascript·harmonyos
铁皮饭盒18 小时前
CPU就行, 老照片变清晰, 本地部署582mb腾讯的模型, 本地部署, 过节了给长辈们变个小魔术
前端·javascript·后端
结局无敌19 小时前
React Native for OpenHarmony 实战:三方库 react-native-progress-steps 的鸿蒙化适配指南
javascript·人工智能·深度学习·react native·react.js·harmonyos