JS–防抖函数、节流函数
防抖函数和节流函数,两者是极其相近的。
首先,根本目的都是为了限制函数触发的频率,提高用户体验;其次,两者的实现原理也是相近的,都可以使用setTimeout、时间戳等方式来实现。
适用的业务场景
防抖函数:
监听浏览器窗口的resize事件;
表单多次重复提交;
输入框实时搜索事件;
点拖拽事件;
监听浏览器滚动事件等等
节流函数:
手机端的下拉加载\上拉刷新更多操作;
表单实时验证;页面数据实时保存等等
实现原理+代码
防抖函数:
当连续触发事件时,在设定的时间内没有再次触发事件,函数A(你希望执行的业务函数)才会被执行一次.如果在设定时间内触发了事件,就重新开始计时.在实现逻辑上,其实就是在两个触发请求的时间间隔大于设定的时间时,总是取前一个触发请求调用函数A 。“活到最后的人才是赢家”。
节流函数:
当连续触发事件时,保证在设定的时间内只调用一次函数A(你希望执行的业务函数)。“先到先得”,即在你设定的时间内,只会执行第一个触发的函数A,如果你在设定的时间内持续触发事件,那么那些事件会被忽略,直到超过设定的时间才会再次执行函数A
防抖函数:
/*个人理解,防抖函数就是网游里角色释放技能时需要进行蓄力/读条,没有蓄力/读条结束就重新按技能,角色会重新蓄力/读条,技能并没有释放出来。*/
// 延时函数版本
function (func, wait) {
let timeout = null
return function() {
const that = this
const args = arguments
if(timeout) {clearTimeout(timeout)}
timeout = setTimeout(function(){
func.apply(that, args)
},wait)
}
}
// 时间戳版本
// 不太建议使用,代码有些冗余
function (func, wait) {
let timeout = null
let oldtime = 0
return function() {
const that = this
const args = arguments
const now = Date.now()
if(now - oldtime < wait) {
clearTimeout(timeout)
}
oldtime = now
timeout = setTimeout(function(){
func.apply(that, args)
},wait)
}
}
节流函数
/*个人理解,节流函数就是网游里角色释放技能后,需要等待技能冷却时间结束才能再次释放技能,期间无论怎么按技能键都无法释放出技能*/
// 时间戳版本
function (func, wait){
let timeout = 0
return function() {
const that = this
const args = arguments
const now = Date.now()
if(now - timeout > wait) {
setTimeout(function() {
func.apply(that, args)
}, wait)
timeout = now
}
}
}
// 延时函数版本
function (func, wait){
let timeout = 0
return function() {
const that = this
const args = arguments
if(!timeout) {
timeout = setTimeout(function() {
func.apply(that, args)
timeout = null
}, wait)
}
}
}
防抖、节流函数的立即执行和非立即执行
防抖函数
立即执行:即多次触发事件,第一次会马上执行函数,之后触发的事件在wait(设置的等待时间)内不会执行函数。相当于法师在读条放火球时,你再按火球技能没有任何效果。
非立即执行:即多次触发事件,只有前一个触发的事件A与后一个触发的事件B之间的时间间隔大于wait(设置的等待时间),前一个触发的事件才会执行一次函数。相当于法师在读条放火球时,你再按火球技能会重置读条。
function debounce(func, wait, immediate) {
let timeout = null
return function() {
// 合并版本
const that = this
const args = arguments
timeout ? clearTimeout(timeout) : immediate && func.apply(that, args)
timeout = setTimeout(function () {
!immediate && func.apply(that, args)
timeout = null
}, wait)
}
}
function debounce(func, wait, immediate) {
let timeout = null
return function() {
const that = this
const args = arguments
if(timeout) {clearTimeout(timeout)}
if(immediate) {
// 立即执行版本
if(!timeout) {
func.apply(that, args)
}
timeout = setTimeout(function (){
timeout = null
},wait)
} else {
// 不立即执行版本
timeout = setTimeout(function(){
func.apply(that, args)
},wait)
}
}
}
节流函数
立即执行:这一部分和防抖函数没有区别。
非立即执行:即多次触发事件,在wait(设置的等待时间)的时间范围内触发的第一个事件才会执行一次函数,其它的会被忽略,直到超出wait(设置的等待时间)。相当于法师释放了一个延时技能(延时3s),技能处于冷却中(冷却3s),在冷却时间内,无法重新释放该技能。
function throttle(func, wait, immediate){
let timeout = null
return function() {
const that = this
const args = arguments
// 合并版本
if(!timeout) {
immediate && func.apply(that, args)
timeout = setTimeout(function() {
!immediate && func.apply(that, args)
timeout = null
}, wait)
}
}
}
function throttle(func, wait, immediate){
let timeout = null
return function() {
const that = this
const args = arguments
if(immediate) {
// 立即执行版本
if(!timeout) {
timeout = setTimeout(function() {
timeout = null
}, wait)
func.apply(that, args)
}
} else {
// 不立即执行版本
if(!timeout) {
timeout = setTimeout(function() {
func.apply(that, args)
timeout = null
}, wait)
}
}
}
}