前端面试之防抖节流(一)

前言

大家好,作者最近准备更新两到三篇文章介绍前端面试必考的防抖节流知识点,着重讲讲防抖节流的应用场景与防抖节流的实现方式源码讲解,此篇是防抖节流文章系列第一篇。

什么是防抖?

通过例子理解防抖

来,让我们先看一个例子

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖节流</title>
</head>
<body>
    <input type = "text" id="inputA">

    <script>
        let inputA = document.getElementById('inputA');
        
       function ajax(content) {
        console.log('ajax request' + content);
       }
       
        inputA.addEventListener('keyup',function(event){
            ajax(event.target.value);
        })
    </script>
</body>
</html>

在这个例子中,你能观察到,在输入框内,每一次键盘按下时,都会触发事件:输出输入框内的内容

防抖在这里的目的是:

当用户停止输入了指定时间间隔delay后(比如停止输入了0.3s),才会输出输入框里的内容,而在这个delay内,输入内容不会触发输出事件。(0.3s的输入不会输出到控制台)

这就是防抖后的效果:输入内容停顿delay后,才会触发输出事件:

防抖的核心概念

这个例子就是防抖的经典例子,现在我给防抖的正式概念,你应该就能大概理解防抖了:

防抖(Debounce) 是一种优化高频事件(如输入、滚动)的技术,在事件触发后延迟执行函数,若在延迟时间内再次触发,则重新计时,确保只执行最后一次操作。常用于搜索框输入、窗口调整等场景。

防抖的应用场景

那么,为什么要实现防抖呢?

对于实际业务中,我们刚才的每一个控制台输出类似于ajax request12314,对应着就是向服务器发送一个请求,如果在没有实现防抖的情况下,就像刚刚的频繁打印,我们可能会在短时间内向服务器发送很多条请求,这样可能会导致服务器宕机,所以,防抖这种优化方式随之就产生了!

在google这种超级大公司,防抖的优化应用就更为重要了: 如图,我们在输入框输入fangdou的这个过程并不会向服务器发送请求,只有输入完"防抖",停顿了几百毫秒后,才会向服务器发送一次请求

同样,国内的百度,搜狗等搜索引擎也应用了防抖。

如何实现防抖?

接下来,我将给出防抖的源码,并且进行详细地解读,确保你能了解到底如何实现防抖:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖节流</title>
</head>
<body>
    <input type="text" id="inputB">
    <script>
       let inputB = document.getElementById('inputB');

       function ajax(content) {
        console.log('ajax request' + content);
       }
       // 通用函数
       function debounce(fn,delay) {
        return function(args) {
            clearTimeout(fn.id)
            fn.id = setTimeout(function(){
                fn(args);
            },delay)
        }
       }

        let debounceAjax = debounce(ajax,200);
        inputB.addEventListener('keyup',function(event){
            debounceAjax(event.target.value)
        })
    </script>
</body>
</html>

源码逐行讲解:

  1. 获取输入框元素
js 复制代码
let inputB = document.getElementById('inputB');

这行代码获取了页面上ID为"inputB"的输入框。

  1. 模拟ajax请求的函数
js 复制代码
function ajax(content) {
  console.log('ajax request' + content);
}

这里只是简单打印,实际应用中这里会是发送网络请求的代码。

  1. 防抖函数
js 复制代码
function debounce(fn, delay) {
  return function(args) {
    clearTimeout(fn.id)
    fn.id = setTimeout(function(){
      fn(args);
    }, delay)
  }
}

debounce函数接收两个参数:要执行的函数fn和等待时间delay

它返回一个新函数,这个新函数会在被调用时:

go 复制代码
清除之前设置的定时器(如果有)
设置一个新的定时器,在`delay`毫秒后执行`fn`  

这两步就是防抖的核心功能

  1. 应用防抖
js 复制代码
let debounceAjax = debounce(ajax, 200);

创建一个防抖版的ajax函数,200毫秒延迟。

  1. 事件监听
js 复制代码
inputB.addEventListener('keyup', function(event){
  debounceAjax(event.target.value)
})

所以当输入框有键盘触发事件时,会调用防抖版的ajax函数debounceAjax,会触发很多次debounceAjax,但是这个debounceAjax会管着向服务器发送请求的次数,在一定的时间延迟后才会真正向服务器发送请求(这里是ajax模拟),从而达到防抖的效果

总结闭包的使用

这段代码中有闭包的应用,我们单独来讲讲

在防抖函数中:

js 复制代码
function debounce(fn, delay) {
  return function(args) {
    clearTimeout(fn.id)
    fn.id = setTimeout(function(){
      fn(args);
    }, delay)
  }
}

闭包的形成

  1. 外层函数debounce(fn, delay) 接收两个参数
  2. 内层函数 :返回的匿名函数 function(args) {...}

闭包的作用

  • 记住参数 :返回的函数记住了fndelay这两个参数
  • 保持状态 :通过fn.id保持了定时器的状态 所以fndelay被内部函数"记住"了,即使debounce函数执行完毕,这些变量也不会被销毁,

闭包的具体表现

debounce(实际是debounceAjax)执行完后,下一次触发键盘事件触发debounceAjax,它还是会记得之前的fn(实际是ajax)和fn.id(实际是ajax.id

作为对象方法 如何处理call

思考下面的场景

js 复制代码
    function debounce(fn,delay) {
    return function(args) {
        clearTimeout(fn.id)
        fn.id = setTimeout(function(){
            fn(args);
        },delay)
    }
   }

let obj = {
    conut:0,
    inc: debounce(function(val){
        console.log(this)
        console.log(this.conut + val)
    },500)
}

obj.inc(2);

this指向什么?

请分析一下这个防抖应用场景中的输出结果:this的指向是什么?

在这里:

  • obj.inc 被赋值为防抖函数的返回函数
  • 原始函数(打印this的函数)被传给debounce

当调用方法obj.inc(2);

  • 实际执行的是debounce返回的函数
  • 最终通过setTimeout调用原始函数(this丢失)

所以由于setTimeout的回调是普通函数调用,this会指向window(非严格模式)或undefined(严格模式)

但是我们期望的是this能够指向obj(因为是通过obj.inc()调用的),所以

解决方案

由于这里涉及到this丢失的问题,所以我们要进行更改,使得this能够正确的指向

js 复制代码
<script>
    function debounce(fn,delay) {
    return function(args) {
        var that = this; 
        clearTimeout(fn.id)
        fn.id = setTimeout(function(){
            fn.call(that,args);
        },delay)
    }
   }

let obj = {
    conut:0,
    inc: debounce(function(val){
        console.log(this)
        console.log(this.conut + val)
    },500)
}

obj.inc(2);
</script>

更改时的分析如下:

步骤 代码位置 this 指向 关键操作
1 obj.inc(2) obj 方法调用,this 自然指向 obj
2 var that = this 保存到 that 变量 闭包捕获当前 this
3 setTimeout 回调内部 原本会丢失指向 通过 fn.call(that,...) 强制绑定

防抖完整代码

所以在防抖的实现中,为了this能够正确的指向目标的对象,var that = this; fn.call(that,args);也是必要的。所以对于第一部分提到的防抖实现代码,在增添了this绑定的代码后,完整的防抖代码如下

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖节流</title>
</head>
<body>
    <input type="text" id="inputB">
    <script>
       let inputB = document.getElementById('inputB');

       function ajax(content) {
        console.log('ajax request' + content);
       }
       // 通用函数
       function debounce(fn,delay) {
        return function(args) {
            var that = this; 
            clearTimeout(fn.id)
            fn.id = setTimeout(function(){
                fn.call(that,args);
            },delay)
        }
       }

        let debounceAjax = debounce(ajax,200);
        inputB.addEventListener('keyup',function(event){
            debounceAjax(event.target.value)
        })
    </script>
</body>
</html>

总结防抖的实现

这篇防抖技术指南就像给你的代码装了个"智能缓冲器"! 我们通过搜索框的例子发现,不加控制的频繁触发事件就像连珠炮一样疯狂请求服务器,而防抖就像个贴心的管家,只在用户真正停下来思考时才帮你执行最后一次操作。核心秘诀就是用闭包这个"记忆大师"配合定时器玩"重置游戏",记得还要用call绑好this避免迷路哦~现在你不仅能手写完美防抖函数,还get了Google同款优化技巧,下次面试官问到这就是你的showtime啦!✨

相关推荐
弗锐土豆7 分钟前
一个基于若依(ruoyi-vue3)的小项目部署记录
前端·vue.js·部署·springcloud·ruoyi·若依
Hilaku10 分钟前
我为什么放弃了“大厂梦”,去了一家“小公司”?
前端·javascript·面试
1undefined212 分钟前
element中的table改造成虚拟列表(不定高),并封装成hooks
前端·vue.js
浅墨momo16 分钟前
搭建第一个Shopify App
前端·程序员
wangpq19 分钟前
element-ui表单使用validateField校验多层循环中的字段
javascript·vue.js
然我20 分钟前
React 事件机制:从代码到原理,彻底搞懂合成事件的核心逻辑
前端·react.js·面试
Codebee20 分钟前
OneCode 组件服务通用协议栈:构建企业级低代码平台的技术基石
前端·前端框架·开源
Running_C20 分钟前
常见web攻击类型
前端·http
jackyChan20 分钟前
ES6 Proxy 性能问题,你真知道吗?🚨
前端·javascript