JavaScript中如何深入理解防抖与节流技术?

更新于
2026-09-26 18:23:37
1阅读来源:SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1700个文字,预计阅读时间需要7分钟。

JavaScript中如何深入理解防抖与节流技术?

目录- 防抖- 节流- 总结- 防抖- 自动门感应- 当有人靠近门时,自动打开,并开始5秒倒计时。- 5秒后,如果门内无人,则重新开始5秒倒计时。- 当事件被触发时,设置一个延迟。

目录
  • 防抖
  • 节流
  • 总结

防抖

自动门感应到有人,打开门,并且开始5秒倒计时,在 5 s 内有另外一个人靠近到门,门感应到人,重新5秒倒计时

当事件被触发时,设定一个延迟,若期间事件又被触发,则重新设定延迟,直到延迟结束,执行动作 (防止多次触发)

web 应用上面

  • 改变页面大小的统计
  • 滚动页面位置的统计
  • 输入框连续输入的请求次数控制

一开始,点击按钮,console.log('pay money')

<body> <button id="btn">click</button> </body> <script> const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } btn.addEventListener('click', payMoney) </script>

定义 debounce

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func) { // 在函数里面返回函数 , 只有当点击的时候才返回该函数 return function () { func() } } btn.addEventListener('click', debounce(payMoney))

设置延时

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func, delay) { return function () { setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

清除延时:未能执行

原因

每次点击的时候就会执行返回函数里面的内容

每次点击的执行函数都是独立的,互不干涉

正因为他们之间没有联系,清除延时在这里完全没有起作用

要让这些独立的执行函数之间有联系,需要用到作用域链(闭包)

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func, delay) { return function () { let timer clearInterval(timer) timer = setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

将 timer 放在返回函数的外围,这样在定义监听事件的同时,就定义了timer变量

因为作用域链,所有独立的执行函数都能访问到这个timer变量

而且现在这个timer变量只创建了一次。是唯一的,我们只不过不断给timer赋值进行延时而已

每个清除延时就是清除上一个定义的延时,相当于多个函数共用同一个外部变量

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func, delay) { let timer return function () { clearInterval(timer) timer = setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

此时的代码,this 是指向 window ?

因为回调的原因,运行时已经在Window下了

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); console.log(this); } function debounce(func, delay) { let timer return function () { clearInterval(timer) timer = setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

解决办法

在 setTimeout 之前将 this 保存下来,此时的 this 是指向按钮的

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); console.log(this); } function debounce(func, delay) { let timer // 只有当点击的时候,才返回此函数,所以 this 是指向按钮的 return function () { let context = this clearInterval(timer) timer = setTimeout(_ => { func.apply(context) }, delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

考虑参数的问题,添加 arg

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); console.log(this); } function debounce(func, delay) { let timer return function () { let context = this let args = arguments clearInterval(timer) timer = setTimeout(_ => { func.apply(context) console.log(context, args); }, delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

节流

先触发一次后,防止接下来多次触发

滚动屏幕:统计用户滚动屏幕的行为来作出相应的网页反应

当用户不断进行滚动,就会不断产生请求,相应也会不断增加,容易导致​ ⛔️ 网络阻塞

我们就可以在触发事件的时候就马上执行任务,然后设定时间间隔限制,在这段时间内不管用户如何进行滚动都忽视操作

在时间到了以后如果监测到用户有滚动行为,再次执行任务。并且设置时间闻隔

首先,写个改变页面尺寸的同时改变页面背景颜色的代码

function coloring() { let r = Math.floor(Math.random() * 255) let g = Math.floor(Math.random() * 255) let b = Math.floor(Math.random() * 255) document.body.style.background = `rgb(${r}, ${g}, ${b})` } window.addEventListener('resize', coloring)

function throttle(func, delay) { let timer return function () { timer = setTimeout(_ => { func() }, delay) } } window.addEventListener('resize', throttle(coloring, 2000))

判断触发的事件是否在时间间隔内

JavaScript中如何深入理解防抖与节流技术?

  • 不在:触发事件
  • 在:不触发事件

function throttle(func, delay) { let timer return function () { // timer 被赋值了,直接返回,即不执行任务 if (timer) { return } // 此时 timer 没被赋值,或 timer 已经执行完了 // 为 timer 赋值进行延时执行 timer = setTimeout(_ => { func() // 延迟执行以后我们要清空timer的值 timer = null }, delay) } } window.addEventListener('resize', throttle(coloring, 2000))

解决 this 指向(虽然当前的这个例子就是在 Window 下的)

function throttle(func, delay) { let timer return function () { let context = this let args = arguments // timer 被赋值了,直接返回,即不执行任务 if (timer) { return } // 此时 timer 没被赋值,或 timer 已经执行完了 // 为 timer 赋值进行延时执行 timer = setTimeout(_ => { func.apply(context, args) // 延迟执行以后我们要清空timer的值 timer = null }, delay) } } window.addEventListener('resize', throttle(coloring, 1000))

节流核心:事件间隔 另一种常见的时间间隔就是用Date对象

function throttle(func, delay) { // 我们要和前一个时间点进行比较才能确定是否已经过了时间间隔 // 在返回函数外围,避免每次执行都被自动修改 let pre = 0 return function () { // 保存执行函数当时的时间 let now = new Date() // 刚开始,一定会执行 if (now - pre > delay) { // 已经过了时间间隔,就可以执行函数了 func() // 执行后,重新设置间隔点 pre = now } } } window.addEventListener('resize', throttle(coloring, 1000))

解决参数问题

function throttle(func, delay) { let pre = 0 return function () { let context = this let args = arguments let now = new Date() if (now - pre > delay) { func.apply(context, args) pre = now } } } window.addEventListener('resize', throttle(coloring, 1000))

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注自由互联的更多内容!

本文共计1700个文字,预计阅读时间需要7分钟。

JavaScript中如何深入理解防抖与节流技术?

目录- 防抖- 节流- 总结- 防抖- 自动门感应- 当有人靠近门时,自动打开,并开始5秒倒计时。- 5秒后,如果门内无人,则重新开始5秒倒计时。- 当事件被触发时,设置一个延迟。

目录
  • 防抖
  • 节流
  • 总结

防抖

自动门感应到有人,打开门,并且开始5秒倒计时,在 5 s 内有另外一个人靠近到门,门感应到人,重新5秒倒计时

当事件被触发时,设定一个延迟,若期间事件又被触发,则重新设定延迟,直到延迟结束,执行动作 (防止多次触发)

web 应用上面

  • 改变页面大小的统计
  • 滚动页面位置的统计
  • 输入框连续输入的请求次数控制

一开始,点击按钮,console.log('pay money')

<body> <button id="btn">click</button> </body> <script> const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } btn.addEventListener('click', payMoney) </script>

定义 debounce

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func) { // 在函数里面返回函数 , 只有当点击的时候才返回该函数 return function () { func() } } btn.addEventListener('click', debounce(payMoney))

设置延时

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func, delay) { return function () { setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

清除延时:未能执行

原因

每次点击的时候就会执行返回函数里面的内容

每次点击的执行函数都是独立的,互不干涉

正因为他们之间没有联系,清除延时在这里完全没有起作用

要让这些独立的执行函数之间有联系,需要用到作用域链(闭包)

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func, delay) { return function () { let timer clearInterval(timer) timer = setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

将 timer 放在返回函数的外围,这样在定义监听事件的同时,就定义了timer变量

因为作用域链,所有独立的执行函数都能访问到这个timer变量

而且现在这个timer变量只创建了一次。是唯一的,我们只不过不断给timer赋值进行延时而已

每个清除延时就是清除上一个定义的延时,相当于多个函数共用同一个外部变量

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func, delay) { let timer return function () { clearInterval(timer) timer = setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

此时的代码,this 是指向 window ?

因为回调的原因,运行时已经在Window下了

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); console.log(this); } function debounce(func, delay) { let timer return function () { clearInterval(timer) timer = setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

解决办法

在 setTimeout 之前将 this 保存下来,此时的 this 是指向按钮的

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); console.log(this); } function debounce(func, delay) { let timer // 只有当点击的时候,才返回此函数,所以 this 是指向按钮的 return function () { let context = this clearInterval(timer) timer = setTimeout(_ => { func.apply(context) }, delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

考虑参数的问题,添加 arg

const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); console.log(this); } function debounce(func, delay) { let timer return function () { let context = this let args = arguments clearInterval(timer) timer = setTimeout(_ => { func.apply(context) console.log(context, args); }, delay) } } btn.addEventListener('click', debounce(payMoney, 1000))

节流

先触发一次后,防止接下来多次触发

滚动屏幕:统计用户滚动屏幕的行为来作出相应的网页反应

当用户不断进行滚动,就会不断产生请求,相应也会不断增加,容易导致​ ⛔️ 网络阻塞

我们就可以在触发事件的时候就马上执行任务,然后设定时间间隔限制,在这段时间内不管用户如何进行滚动都忽视操作

在时间到了以后如果监测到用户有滚动行为,再次执行任务。并且设置时间闻隔

首先,写个改变页面尺寸的同时改变页面背景颜色的代码

function coloring() { let r = Math.floor(Math.random() * 255) let g = Math.floor(Math.random() * 255) let b = Math.floor(Math.random() * 255) document.body.style.background = `rgb(${r}, ${g}, ${b})` } window.addEventListener('resize', coloring)

function throttle(func, delay) { let timer return function () { timer = setTimeout(_ => { func() }, delay) } } window.addEventListener('resize', throttle(coloring, 2000))

判断触发的事件是否在时间间隔内

JavaScript中如何深入理解防抖与节流技术?

  • 不在:触发事件
  • 在:不触发事件

function throttle(func, delay) { let timer return function () { // timer 被赋值了,直接返回,即不执行任务 if (timer) { return } // 此时 timer 没被赋值,或 timer 已经执行完了 // 为 timer 赋值进行延时执行 timer = setTimeout(_ => { func() // 延迟执行以后我们要清空timer的值 timer = null }, delay) } } window.addEventListener('resize', throttle(coloring, 2000))

解决 this 指向(虽然当前的这个例子就是在 Window 下的)

function throttle(func, delay) { let timer return function () { let context = this let args = arguments // timer 被赋值了,直接返回,即不执行任务 if (timer) { return } // 此时 timer 没被赋值,或 timer 已经执行完了 // 为 timer 赋值进行延时执行 timer = setTimeout(_ => { func.apply(context, args) // 延迟执行以后我们要清空timer的值 timer = null }, delay) } } window.addEventListener('resize', throttle(coloring, 1000))

节流核心:事件间隔 另一种常见的时间间隔就是用Date对象

function throttle(func, delay) { // 我们要和前一个时间点进行比较才能确定是否已经过了时间间隔 // 在返回函数外围,避免每次执行都被自动修改 let pre = 0 return function () { // 保存执行函数当时的时间 let now = new Date() // 刚开始,一定会执行 if (now - pre > delay) { // 已经过了时间间隔,就可以执行函数了 func() // 执行后,重新设置间隔点 pre = now } } } window.addEventListener('resize', throttle(coloring, 1000))

解决参数问题

function throttle(func, delay) { let pre = 0 return function () { let context = this let args = arguments let now = new Date() if (now - pre > delay) { func.apply(context, args) pre = now } } } window.addEventListener('resize', throttle(coloring, 1000))

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注自由互联的更多内容!