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

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

前言

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

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

```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>

image.png

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

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

<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时的值的引用。
  1. 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): 当用户快速点击按钮时,点击事件可能被间隔触发。通过节流,可以保证按钮点击事件处理函数在一定的时间内间隔内只执行一次,防止过快的连续点击。

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

<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

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

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关键字指向正确:

<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>

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

相关文章
|
2月前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
64 20
Android经典面试题之图片Bitmap怎么做优化
|
2月前
|
SQL 关系型数据库 MySQL
面试官:limit 100w,10为什么慢?如何优化?
面试官:limit 100w,10为什么慢?如何优化?
190 2
面试官:limit 100w,10为什么慢?如何优化?
|
3月前
|
运维 监控 算法
[go 面试] 优化线上故障排查与性能问题的方法
[go 面试] 优化线上故障排查与性能问题的方法
|
4月前
|
缓存 Prometheus 监控
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
92 3
|
4月前
|
SQL 监控 Java
Java面试题:简述数据库性能优化的常见手段,如索引优化、SQL语句优化等。
Java面试题:简述数据库性能优化的常见手段,如索引优化、SQL语句优化等。
295 0
|
4月前
|
设计模式 存储 缓存
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
56 0
|
4月前
|
设计模式 并行计算 安全
Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
50 0
|
4月前
|
设计模式 安全 Java
Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
106 0
|
3月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
8天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?