面试时让你手写一个防抖和节流优化,你能写出来吗?(二)

前言

今天我们来聊聊防抖和节流,它们的学名为再次防抖(Debouncing)限制流节(Throttting),防抖和节流是前端开发中用于优化页面性能和提高用户体验的第二个技术。它们的目标是减少不必要的函数调用,从而减少资源消耗,提高页面数响应速度,以及提升用户交互体验。在面试过程中,面试官若是询问了你这两个概念,很大可能会叫你手写一下它们,来考察你对它们的理解。

在上一篇文章中,我们聊到了如何给程序添加一个防抖,以提高性能并避免不必要的资源浪费。 我们先来回顾一下:

js 复制代码
```js
<button id="btn">提交</button>

    <script>
        let btn = document.getElementById('btn')
        btn.addEventListener('click', debounce(send, 1000))

        function send(){
            console.log('提交完成');
        }

        function debounce(fn, delay){
            let timer;

            return function(){
                if(timer)   clearTimeout(timer)
                timer = setTimeout(function(){
                    fn()
                }, delay)
            }
        }

    </script>

原理在规定的时间内没有第二次的操作,才执行逻辑

我们手写的这个防抖并没有完美,因为若是 send()接受参数,我们并不能传入参数,如果send()函数上有this关键字,我们要让this指向 btn.addEventListener,那我们应该如何加强一下我们的手写防抖呢?

js 复制代码
    <button id="btn">提交</button>

    <script>
        let btn = document.getElementById('btn')
        btn.addEventListener('click', debounce(send, 1000))

        function send(){
            console.log('提交完成');
        }

        function debounce(fn, delay){
            let timer;

            return function(){
                let args = arguments
                let _this = this
                if(timer)   clearTimeout(timer)
                timer = setTimeout(function(){
                    fn.call(_this, ...args)
                }, delay)
            }
        }

    </script>

在提供的代码片段中,thisarguments具有具体含义:

  1. this关键词:

    • 在 JavaScript 中,函数内部的值this取决于函数的调用方式。
    • 当函数作为对象的方法被调用时,this指的是调用该方法的对象。
    • 当单独调用函数时,this指的是全局对象(位于window浏览器环境中)。
    • debounce函数中,_this用于存储对函数执行this时的值的引用。
  2. arguments目的:

    • arguments对象是一个类似数组的对象,它保存传递给函数的所有参数。
    • debounce函数中,arguments用于捕获传递给去抖函数的所有参数 ( fn)。这是因为该debounce函数被设计为通用的,并且可以与任何函数和任意数量的参数一起使用。
    • 该行创建对函数内部对象的let args = arguments引用。arguments debounce
  • let args = arguments:此行捕获传递给去抖函数的所有参数并将它们存储在args变量中。
  • let _this = this:该行捕获函数执行时的值debounce并将其存储在_this变量中。稍后在回调中使用它setTimeout
  • fn.call(_this, ...args):此行使用捕获的上下文 ( )_this和参数 ( )调用原始函数( ...args)。该call方法用于this在调用函数时显式设置 的值。

限制流节(Throttting)

节流是指在一定的时间间隔内,无论事件触发频率多高,只执行一次。节流的目的是控制函数的执行频率,保证在指定的时间间隔内不会执行超过一次。典型的应用场景包括处理滚动事件、窗口调整事件等。

实现节流的方法可以是计时器,也可以是记录上一次执行的时间。每次事件触发时,都会检查离开上一次执行的时间是否超过指定的时间间隔,如果是,则执行相应的操作,并更新上一次执行的计时器。

我们可以用一句话来概括一下,帮助小伙伴们更好的理解: 在规定的时间内只执行一次

节流的应用:

  1. 滚动事件(Scrolling): 当用户滚动页面时,滚动事件可能被间隔触发。使用节流可以保证滚动事件处理函数只在一定的时间间隔内执行一次,避免过多的计算和渲染,提高页面性能。

  2. 窗口调整事件(Resize): 窗口调整事件在用户调整器窗口大小时触发,也可以间隙触发。通过节流,可以保证窗口调整事件处理功能不会过度间隙地执行。

  3. 输入框输入事件(Input): 处理用户在输入框输入文字时,输入事件会关闭触发。通过节流,可以延迟执行处理函数,避免在每次输入时都立即进行相关操作,比如搜索建议的显示。

  4. 按钮点击事件(Click): 当用户快速点击按钮时,点击事件可能被间隔触发。通过节流,可以保证按钮点击事件处理函数在一定的时间内间隔内只执行一次,防止过快的连续点击。

而这个例子中我们可以使用节流,当用户快速点击按钮时,点击事件可能被间隔触发。通过节流,可以保证按钮点击事件处理函数在一定的时间内间隔内只执行一次,防止过快的连续点击。

js 复制代码
<button id="btn">提交</button>

    <script>
        let btn = document.getElementById('btn')
        function send(){
            console.log('提交了');
        }
        btn.addEventListener('click',throttle(send, 1000) )

        function throttle(fn, delay){
            let prevTime = Date.now()
            return function(){
                if(Date.now() - prevTime > delay){
                    fn()
                    prevTime = Date.now()
                }
            }
        }
    </script>

代码中包含了一个闭包,闭包是指在一个函数内部创建另一个函数,并且内部函数可以访问外部函数的变量,当内部函数被返回到外部函数之外时,即使外部函数执行结束了, 但是内部函数引用了外部函数的变量,那么这些变量依旧会被保存在内层中。这里,函数就是一个闭包throttle

闭包是一个十分重要的概念,如果小伙伴不太理解闭包的话,可以看我的文章,这篇文章对闭包有着详细的解释:# 前端面试:聊聊闭包 一盏茶的功夫让你彻底掌握闭包!

js 复制代码
function throttle(fn, delay) {
  // 用于存储上一次函数执行的时间戳
  let prevTime = Date.now();

  // 返回一个新的函数,该函数会进行节流控制
  return function() {
    // 当前时间戳
    let currentTime = Date.now();

    // 判断是否超过了指定的时间间隔
    if (currentTime - prevTime > delay) {
      // 如果超过了时间间隔,执行传入的函数 fn
      fn();

      // 更新上一次函数执行的时间戳
      prevTime = Date.now();
    }
  };
}
  1. let prevTime = Date.now(); 在节流函数中,使用一个变量 prevTime 来存储上一次函数执行的时间戳。初始时,将其设置为当前时间。
  2. return function() { ... } 节流函数返回一个新的函数,这个函数会进行节流控制。
  3. let currentTime = Date.now(); 在新返回的函数中,获取当前时间戳。
  4. if (currentTime - prevTime > delay) { ... } 判断当前时间与上一次函数执行的时间间隔是否超过了指定的 delay。如果超过了,就执行传入的函数 fn
  5. fn(); 如果时间间隔超过了指定的 delay,则执行传入的函数 fn
  6. prevTime = Date.now(); 更新上一次函数执行的时间戳,以便下一次判断是否超过时间间隔。

这样,通过使用这个节流函数,可以确保在调用返回的函数时,传入的函数 fn 在指定的时间间隔内最多执行一次。这有助于控制频繁触发的事件,以提高性能和用户体验。

当然,我们也需要可以接受send传入的参数,和send中的this关键字指向正确:

js 复制代码
<button id="btn">提交</button>

    <script>
        let btn = document.getElementById('btn')
        function send(){
            console.log('提交了');
        }
        btn.addEventListener('click',throttle(send, 1000) )

        function throttle(fn, delay){
            let prevTime = Date.now()
            return function(){
                if(Date.now() - prevTime > delay){
                    fn.apply(this, arguments)
                    prevTime = Date.now()
                }
            }
        }
    </script>

今天的内容就到这啦,如果你觉得小编写的还不错的话,或者对你有所启发,请给小编一个辛苦的赞吧

相关推荐
F-2H1 小时前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
gqkmiss2 小时前
Chrome 浏览器插件获取网页 iframe 中的 window 对象
前端·chrome·iframe·postmessage·chrome 插件
m0_748247554 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255024 小时前
前端常用算法集合
前端·算法
真的很上进5 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203985 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2345 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
测试老哥5 小时前
外包干了两年,技术退步明显。。。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
如若1236 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~7 小时前
npm error code ETIMEDOUT
前端·npm·node.js