前言
今天我们来聊聊防抖和节流,它们的学名为再次防抖(Debouncing)和限制流节(Throttting),防抖和节流是前端开发中用于优化页面性能和提高用户体验的第二个技术。它们的目标是减少不必要的函数调用,从而减少资源消耗,提高页面数响应速度,以及提升用户交互体验。在面试过程中,面试官若是询问了你这两个概念,很大可能会叫你手写一下它们,来考察你对它们的理解。
再次防抖(Debouncing)
防抖是一种函数执行频率的方法。当一个事件被触发时,防抖会延迟一定时间执行相应的处理函数。如果在这个延迟延迟触发了相同的事件,则重新计时。这对于处理用户输入、滚动事件等触发操作间隔非常有用,可以防止过多的函数调用,从而提高性能并避免不必要的资源浪费。
我们可以用一句话来概括一下,帮助小伙伴们更好的理解:在规定的时间内没有第二次的操作,才执行逻辑
防抖的应用:
防抖的应用非常广泛,特别是在处理一些触发的事件时,可以有效地降低函数执行的频率,提升一些性能和用户体验。以下是防抖的典型应用场景:
- 输入框搜索: 在用户输入搜索关键词时,使用防抖可以延迟搜索请求的触发,保证用户停止输入一段时间后才执行实际的搜索操作。这减少了无谓的搜索请求,减轻了服务器负担。
- 窗口大小调整: 用户当调整浏览器窗口大小时,触发调整大小事件可能会非常关闭。通过防抖,可以保证只有在用户停止调整窗口大小后才触发相应的处理函数,避免不必要的计算和布局更新。
- 滚动事件: 处理页面滚动事件时
我们来一个简单的例子来理解一下防抖:
有时候我们在网络上提交一份东西时,有些人会一下点击个五六遍提交,或是在网络不好时,我们点击提交是没有反应,就会多点击几下再尝试,但是这样,可能会导致大量的请求,影响页面性能。
就比如这样:
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button id="btn">提交</button> <script> let btn = document.getElementById('btn') btn.addEventListener('click', send) function send(){ console.log('提交完成'); } </script> </body> </html>
这里,假设send
是我们点击提交后发送的请求,可以看到,我们每点击一次提交,就会发送一次请求,这样会造成不好的影响,那我们应该怎么样解决这个问题呢?那我们就要设置防抖
防抖
- 原理: 在规定的时间内没有第二次的操作,才执行逻辑
这句话放到这个例子中来理解的话,就是说当我们点击了提交之后,在规定的时间内没有再次点击提交,才会发送请求,比如我们规定时间为一秒,当我们点击提交之后,假设我们在0.5s的时候点击了提交,那么第一次提交的请求就不会发送,点击第二次提交的一秒钟之内如果没有���击第三次提交,那么请求将会再1,5s时后发送。也就说,如果你在规定的时间内一直点击提交按钮,那么一次请求都不会发送,直到你点击最后一次提交时,过了1s就会发送请求。
<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>
代码中包含了一个闭包,闭包是指在一个函数内部创建另一个函数,并且内部函数可以访问外部函数的变量,当内部函数被返回到外部函数之外时,即使外部函数执行结束了, 但是内部函数引用了外部函数的变量,那么这些变量依旧会被保存在内层中。这里,函数就是一个闭包debounce
。
闭包是一个十分重要的概念,如果小伙伴不太理解闭包的话,可以看我的文章,这篇文章对闭包有着详细的解释: # 前端面试:聊聊闭包 一盏茶的功夫让你彻底掌握闭包!
function debounce(fn, delay) { let timer; // 在闭包内定义的变量,被内部函数访问 // 返回的是一个新的函数,这个新函数是闭包的一部分 return function() { // 在内部函数中可以访问外部函数的变量,例如这里的 timer if (timer) clearTimeout(timer); // 设置一个新的计时器,在指定的延迟时间后执行传入的函数 fn timer = setTimeout(function() { fn(); // 执行传入的函数 }, delay); }; }
现在让我们详细解释一下闭包的一些重要概念:
- 函数内部定义的变量: 在
debounce
函数内部,有一个变量timer
被定义。由于内部函数(通过return function() {...}
返回的那个函数)可以访问外部函数的变量,所以timer
成为了一个闭包变量。这意味着在返回的函数中,您可以使用和修改timer
变量,并且其生命周期结束了debounce
函数的调用。 - 返回一个新的函数:
debounce
函数返回了一个新的函数,这个函数就是闭包的一部分。这个新函数可以访问外部函数的变量(这里是和),即使在函数执行完毕之后,timer
这些delay
变量debounce
的状态也被保留了。 - 使用外部变量: 在返回的函数内,使用了
timer
变量。该变量在每次函数被调用时都被检查和更新,确保在指定的延迟时间内只执行一次指定的函数fn
。
通过这种方式,debounce
函数可以灵活地创建具有定制延迟执行行为的函数。在这里,它用于一个防抖函数,以确保创建在指定的时间内只执行一次确定的函数。
让我们来看看效果:
当我们点击了最后一次提交后,过了规定时间才会执行请求
下一篇文章我们会继续拓展一下防抖,并且讲讲节流。
总结
防抖
在规定的时间内没有第二次的操作,才执行逻辑