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打印一次滚动的距离
相关推荐
NaRciCiSSuS9 分钟前
第一章-JavaScript简介
开发语言·javascript·ecmascript
XF鸭15 分钟前
HTML-CSS 入门介绍
服务器·前端·javascript
kinlon.liu43 分钟前
基于 Nginx + Spring Boot + Vue + JPA 的网站安全防护指南
网络·vue.js·spring boot·nginx·安全
forwardMyLife1 小时前
element-plus 的form表单组件之el-radio(单选按钮组件)
前端·javascript·vue.js
fs哆哆1 小时前
ExcelVBA运用Excel的【条件格式】(二)
linux·运维·服务器·前端·excel
码农小野1 小时前
基于Vue.js和SpringBoot的地方美食分享网站系统设计与实现
vue.js·spring boot·美食
安冬的码畜日常1 小时前
【CSS in Depth 2精译】2.5 无单位的数值与行高
前端·css
ilisi_1 小时前
导航栏样式,盒子模型
前端·javascript·css
吉吉安2 小时前
grid布局下的展开/收缩过渡效果【vue/已验证可正常运行】
前端·javascript·vue.js
梦凡尘2 小时前
Vue3 对跳转 同一路由传入不同参数的页面分别进行缓存
前端·javascript·vue.js