概念
函数节流:指定时间间隔只会执行一次任务
函数防抖:任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行
函数节流
这里以判断页面是否滚动到底部为例,普通的做法就是监听 window 对象的 scroll 事件,然后在函数体中写入判断是否滚动到底部的逻辑:
$(window).on('scroll', function () {
// 判断是否滚动到底部的逻辑
let pageHeight = $('body').height(),
scrollTop = $(window).scrollTop(),
winHeight = $(window).height,
thresold = pageHeight - scrollTop - winHeight
if(thresold > -100 && thresold <= 20) {
console.log('end')
}
})
这样做的缺点是比较消耗性能,因为当在滚动的时候,浏览器会无时无刻在计算判断是否滚动到底部的逻辑。
在实际场景中,滚动过程中,每隔一段时间再去计算这个判断逻辑。函数节流所做的工作就是每隔一段时间去执行一次原本需要无时无刻地在执行的函数,所以在滚动事件中引入函数节流是一个非常好的实践:
$(window).on('scroll', throttle(function () {
// 判断是否滚动到底部的逻辑
let pageHeight = $('body').height(),
scrollTop = $(window).scrollTop(),
winHeight = $(window).height,
thresold = pageHeight - scrollTop - winHeight
if(thresold > -100 && thresold <= 20) {
console.log('end')
}
}))
加上函数节流后,当页面再滚动的时候,每隔 300ms 才会去执行一次判断逻辑。
简单来说函数节流就是通过闭包保存一个标记(canRun = true
),在函数的开头判断这个标记是否为 true,如果为 true 的话继续执行这个函数,否则 return 掉,判断完标记后立刻把这个标记设置为 false,然后把外部传入的函数的执行包在一个 setTimeout 中,最后在 setTimeout 执行完毕后再把标记设置为 true ,表示可以执行下一的循环了,当 setTimeout 还未执行的时候, canRun 这个标记始终为 false,在开头的判断中被 return 掉。
function throttle(fn, interval = 300) {
let canRun = true
// 闭包确保内部的 canRun 不会消失
return function () {
if(!canRun) return
canRun = false
setTimeout(() => {
fn.apply(this, arguments)
canRun = true
}, interval)
}
}
函数防抖
这里以用户注册时验证用户名是否被占用为例,如今很多网站为了提高用户体验,不会再输入框失去焦点的时候再去判断用户名是否被占用,而是在输入的时候就判断这个用户名是否已经被注册:
$('input.user-name').on('input', function () {
$.ajax({
url: 'https://just.com/check',
method: 'post',
data: {
username: $(this).val()
},
success(data) {
if(data.isRegistered) {
$('.tips').text('该用户名已被注册')
} else {
$('.tips').text('该用户名未被注册')
}
},
error(error) {
console.log(error)
}
})
})
很明显,这样的做不好的地方在于当用户输入第一个字符的时候,就开始请求判断,不仅对服务器的压力增大,对用户体验也未必比以前好。理想做法应该是,当用户输入第一个字符后的一段时间内如果还有字符输入,则不去请求判断用户名是否被占用。在这里引入函数防抖就可以完美解决问题:
$('input.user-name').on('input', debounce(function () {
$.ajax({
url: 'https://just.com/check',
method: 'post',
data: {
username: $(this).val()
},
success(data) {
if(data.isRegistered) {
$('.tips').text('该用户名已被注册')
} else {
$('.tips').text('该用户名未被注册')
}
},
error(error) {
console.log(error)
}
})
}))
防抖函数的原理就是,通过一个闭包保存一个标记用来存放 setTimeout 返回的值,每当用户输入的时候把前一个 setTimeout clear 掉,然后又创建一个新的 setTimeout,这样就可以保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数。
function debounce(fn, interval = 300) {
let timeout = null
return function () {
clearTimeout(timeout)
timeout = setTimeout(() => {
fn.apply(this, arguments)
}, interval)
}
}
总结
函数节流与函数防抖的目的就是为了节约计算机资源。