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打印一次滚动的距离
相关推荐
也无晴也无风雨25 分钟前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational2 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤5 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui