js语法---理解防抖原理和实现方法

什么是防抖(节流)

在实际的网页交互中,如果一个事件高频率的触发,这会占用很多内存资源,但是实际上又并不需要监听触发如此多次这个事件(比如说,在抢有限数量的优惠券时,用户往往会提前在短时间内高频率的点击按钮,但是我只需要接受多次点击中的一次点击,判断有没有抢到即可),这个时候就需要防抖来减少监听的次数,

防抖就是在多次触发事件时,减少对冗余事件的监听(主要包括,只保留一次监听一定事件内只触发一次监听),

防抖使用场景 :在原有需求能实现的前提下减少多余操作

原理和示例

事件案例

以下是一个点击事件的展示,我们希望判断这个按钮有没有被点击(只要又打印结果即可)

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>
  <button id="bt">点击</button>
  <script src="index.js"></script>
</body>
</html>
javascript 复制代码
const bt = document.getElementById('bt');
const click = () => {
  console.log('点击了按钮');
}

bt.addEventListener('click', click);

可以看到短时间内用户可能会多次点击按钮,但是我们只需要一次打印就可以判断出用户是否点击了按钮,所以这里有16次的事件冗余。

设置防抖

思路:在用户多次点击按钮时,我们选择保留最后一次事件触发,即在点击到事件触发这段时间,如果用户再点击,则将上一次点击的操作取消,而等待执行新点击的操作,以此类推;

javascript 复制代码
const bt = document.getElementById('bt');
let timer;// 声明一个全局的定时器变量
const click = () => {
  clearTimeout(timer);// 清除之前的定时器
  timer = setTimeout(() => { // 延时触发操作
    console.log('点击了按钮');
  }, 500);
}

bt.addEventListener('click', click);

这里的代码要注意这个定时器变量,它要在click函数体外,因为它要在click触发时保存上一次的定时器,并对其实现清楚,如果出现在click函数体内,则会因为每次都产生新的定时器且无法捕获到上一次的定时器,而导致所有打印延迟生效,没有被清楚

此时在点击按钮时,不会立刻打印结果,而多次点击时,由于旧的定时器不断被清楚,打印操作都不会被触发,直到停下点击之后的0.5s才会出现打印结果

封装一个防抖函数

了解了防抖的基本实现,我们封装一个防抖函数以便于对函数的时间间隔防抖功能实现,

防抖函数,它应该接受两个参数,一个是要执行的函数一个是执行函数的间隔,并且返回一个有防抖效果的函数

javascript 复制代码
/**
 * @param fun 要执行的操作
 * @param time 执行的间隔
 * @return 返回有防抖效果的原操作
 * */
const FD = (fun, time) => {
  let timer;// 闭包储存定时器变量
  return function () {
    if (!timer) {
      timer = setInterval(() => {
        fun();
        clearInterval(timer);// 清除定时器
        timer = null;// 重置定时器变量
      }, time);
    }
  }
}

因为要有一个变量来保存定时器的状态,所以这采用闭包的形式保存这个timer,这样每次执行这个FD的返回函数时,timer的值都会保留下来而不是被覆盖,

关于闭包的解释可以参考:js闭包------简单理解闭包含义_js 闭包累加-CSDN博客

使用实例

监听一个页面滚动的事件,打印出滚动的高度,用防抖减少打印的次数

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 style="height: 200vh;">
  <button id="bt">点击</button>
  <script src="index.js"></script>
</body>
</html>
javascript 复制代码
window.onscroll = () => {
  console.log('滚动的距离', window.scrollY);
}

这里即使是滚动一小段距离也会多次触发打印

滚动了300的距离就触发多次打印,

javascript 复制代码
/**
 * @param fun 要执行的操作
 * @param time 执行的间隔
 * @return 返回有防抖效果的原操作
 * */
const FD = (fun, time) => {
  let timer;// 闭包储存定时器变量
  return function () {
    if (!timer) {
      timer = setInterval(() => {
        fun();
        clearInterval(timer);// 清除定时器
        timer = null;// 重置定时器变量
      }, time);
    }
  }
}

window.onscroll = FD(() => {
  console.log('滚动的距离', window.scrollY);
},500);

有了防抖之后,0.5秒只触发一次打印,大大减少了事件触发

完整代码展示

index.html:

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 style="height: 200vh;">
  <button id="bt">点击</button>
  <script src="index.js"></script>
</body>
</html>

index.js:

javascript 复制代码
// 1.声明一个变量,储存定时器,
// 2.在点击事件中,先清除之前的定时器,再设置新的定时器,
// 3.设置定时器延时操作,相当于等待操作执行结束,才会执行下一次操作(防抖)

const bt = document.getElementById('bt');
let timer;// 声明一个全局的定时器变量
const click = () => {
  clearTimeout(timer);// 清除之前的定时器
  timer = setTimeout(() => { // 延时触发操作
    console.log('点击了按钮');
  }, 500);
}

bt.addEventListener('click', click);

// 多次点击,只执行最后一次操作(节流)


// 节流

/**
 * @param fun 要执行的操作
 * @param time 执行的间隔
 * @return 返回有防抖效果的原操作
 * */
const FD = (fun, time) => {
  let timer;// 闭包储存定时器变量
  return function () {
    if (!timer) {
      timer = setInterval(() => {
        fun();
        clearInterval(timer);// 清除定时器
        timer = null;// 重置定时器变量
      }, time);
    }
  }
}


// window.onscroll = () => {
//   console.log('滚动的距离', window.scrollY);
// }

window.onscroll = FD(() => {
  console.log('滚动的距离', window.scrollY);
},500);

// 滚动时,每隔500ms打印一次滚动的距离
相关推荐
liuyang___4 分钟前
分享一下这几天在公司学到的东西
前端
rocky1915 分钟前
谷歌浏览器插件 录制菜单路由跳转行为 事件重复解决方案
前端·javascript
佳腾_9 分钟前
【web服务_负载均衡Nginx】一、Nginx 基础与核心概念解析
前端·nginx·负载均衡
风中飘爻15 分钟前
MySQL入门:数据操作CURD
前端·bootstrap·html
rocky19123 分钟前
谷歌浏览器插件 录制元素拖动事件
前端·javascript
coder777741 分钟前
js逆向分享
javascript·爬虫·python·算法·安全
emoji1111111 小时前
vue3、原生html交互传值
vue.js·html·交互
nothingbutluck4641 小时前
2025.4.10 html有序、无序、定义列表、音视频标签
前端·html·音视频
爱上python的猴子1 小时前
chrome中的copy xpath 与copy full xpath的区别
前端·chrome
Lysun0012 小时前
dispaly: inline-flex 和 display: flex 的区别
前端·javascript·css