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)
    }
}
相关推荐
qq_589666055 小时前
TypeScript 完整入门教程
前端·javascript·typescript
星栈独行7 小时前
翻完 Pi 源码:它和 Codex、Claude Code 有何不同
开发语言·javascript·人工智能·程序人生
落落Plus8 小时前
浏览器缓存机制详解
javascript·缓存·浏览器缓存
浅水壁虎10 小时前
vue基础(第二章 )
前端·javascript·vue.js
界面开发小八哥10 小时前
界面控件DevExtreme v26.1新版亮点——支持Angular 22
前端·javascript·angular.js·devexpress·ui开发·devextreme
mONESY11 小时前
React useState 核心原理与最佳实践:从异步更新到性能优化全解
javascript
腻害兔11 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:CRM 客户关系模块深度解析——从线索到回款,一套完整的 B2B 销售闭环是怎么搭的?
java·前端·javascript·vue.js·产品经理·ai编程
gis开发之家12 小时前
《Vue3 从入门到大神37篇》Vue3 源码详解(七):watch 与 watchEffect 源码对比——副作用是如何被追踪的?
javascript·前端框架·vue3·vue3源码
bloglin9999914 小时前
langchain 和 langgraph 和 react
javascript·react.js·langchain
触底反弹14 小时前
🚀 删了数据刷新又回来?3 组件 × 4 回调 × 3 坑讲透 React 父子通信
前端·javascript·react.js