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)
    }
}
相关推荐
子兮曰14 小时前
Chrome 权限元素实战:144 与 151 的渐进增强写法
前端·javascript·chrome
强尼银手14 小时前
我花一天做了个格斗游戏原型,判定全对,然后被「美术」劝退了
前端·javascript
总要学点什么15 小时前
第98篇:三维特效-材质线效果——给一条线换十种材质,虚线的图案只填了一半
开发语言·前端·javascript·ecmascript·gis·webgl·材质
OpsEye16 小时前
混合云 AI 架构,如何统一做敏感内容拦截?
javascript·ai编程
Funlang18 小时前
给机器写的 JavaScript 造个笼子
开发语言·javascript·ecmascript
古茗前端团队18 小时前
由 key 引发的思考
前端·javascript·react.js
kyriewen18 小时前
我扫了 422 个仓库,近一半 CI 在裸装依赖
前端·javascript·程序员
vipxieliang1 天前
JavaScript 迭代器与生成器:控制异步与惰性求值
前端·javascript
Hilaku1 天前
Event Loop 这篇你看完就不用再看别的了!
前端·javascript·程序员
晓得迷路了1 天前
栗子前端技术周刊第 153 期 - Vitest 5.1、Remix v3.2、Node.js 27 Alpha...
前端·javascript·vite