防抖和节流严格来讲属于性能优化的相关内容,但实际上遇到的频率相当高,如果不处理的话势必会造成浏览器的卡掉,所以这些知识还是需要掌握的。 很多涉及到的是列表出现有滚动条得时候,有一个按钮可以回到顶部,这个按钮只会在滚动到距离顶部一定位置之后才出现,那么我们现在抽象出这个功能需求,---监听浏览器滚动事件,返回当前滚动条与顶部的距离,
上代码
function freshTop () {
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
}
window.onscroll = freshTop
在运行得时候你会发现这个freshTop函数的默认执行频率过于频繁
函数防抖(debounce)
原理:将若干函数调用合成为一次,并在给定时间过去之后,或者连续事件完全触发完成之后,调用一次(仅仅只会调用一次)
举个例子:
滚动scroll事件,不停滑动滚轮会连续触发多次滚动事件,从而调用绑定的回调函数,我们需求是,不停止滚动时,触发一次回调,这时候可以使用函数防抖;
<style>
* {
padding: 0;
margin: 0;
}
.box {
width: 800px;
height: 1200px;
}
</style>
<body>
<div class="container">
<div class="box" style="background: greenyellow"></div>
<div class="box" style="background: hotpink"></div>
<div class="box" style="background: deeppink"></div>
<div class="box" style="background: seagreen"></div>
</div>
<script>
window.onload = function() {
const debounce = function(fn, delay) {
let timer = null
return function() {
const context = this
let args = arguments
clearTimeout(timer) // 每次调用debounce函数都会将前一次的timer清空,确保只执行一次
timer = setTimeout(() => {
fn.apply(context, args)
}, delay)
}
}
let num = 0
function scrollTop() {
num++
console.log(`num当前是 ${num}`)
}
// 此处的触发时间间隔设置为500
// document.addEventListener('scroll', debounce(scrollTop, 500))
document.addEventListener('scroll', scrollTop)
}
</script>
</body>
效果如下:
当触发时间间隔很小,如果匀速不间断的滚动,不断触发scroll事件,用debounce函数防抖,num在一段时间里只改变了一次,直接调用scrollTop函数的效果截图,
浏览器在处理setTimeout 最短时间间隔是4毫秒和setInterval最短时间间隔是10毫秒当小于10毫秒的时间间隔会被调整到10毫秒,两者都有最小的时间间隔,如果未优化,scroll事件频繁触发的时间间隔也是这个最小时间间隔,可以说当我们在debounce函数中的间隔事件设置不恰当(小于这个最小时间间隔),会使debounce无效.
函数节流 throttle
当达到了一定的时间间隔就会执行一次,可以理解为减少执行频率以scroll滚动事件为例,滚动事件是及其消耗浏览器性能的,不停触发,在移动端通过scroll实现分页,不断滚动,我们不希望不断发送请求,只有当达到某个条件,比如,距离手机窗口底部200px才发送一个请求,接下来就是展示新页面的请求,一直滚动,如此反复,这个时候就得用到函数节流.
<style>
* {
padding: 0;
margin: 0;
}
.box {
width: 800px;
height: 1200px;
}
</style>
<body>
<div class="container">
<div class="box" style="background: greenyellow"></div>
<div class="box" style="background: hotpink"></div>
<div class="box" style="background: deeppink"></div>
<div class="box" style="background: seagreen"></div>
</div>
<script>
window.onload = function() {
const throttle = function(func,delay) {
let timer = null;
return function () {
const context = this; //保存事件的触发对象
let args = arguments;
if(!timer){ //必须确保上一次定时器执行完毕
timer = setTimeout(()=>{
func.call(context,...args);
timer=null;
//及时清理,表示执行完毕,clearTimeout后timer仍有值
//这是一个容易错误的地方
},delay)
}
}
}
let num = 0
function scrollTop() {
num++
console.log(`num当前是 ${num}`)
}
document.addEventListener('scroll', throttle(scrollTop, 500))
}
</script>
</body>
在debounce中,在执行setTimeout函数之前总会将timer用setTimeout清除, 取消延迟代码块,确保只执行一次; 在throttle中,只要timer存在就会执行setTimeout,在setTimeout内部每次清空这个timer, 但是延迟代码块已将执行了,确保一定频率执行一次 我们依旧可以在html页面中进行测试scroll事件,运行结果如图
实际中方法也不止一个
方法一: 时间戳实现
const throttle = function(fn, delay) {
let newTime = Date.now()
return function() {
const context = this
let arg = arguments
let newTime2 = Date.now()
if (newTime2 - newTime >= delay) {
fn.apply(context, arg)
newTime = Date.now()
}
}
}
方法二:定时器实现
const throttle = function(fn,delay) {
let timer = null
return function() {
const context = this
let arg = arguments
if(!timer) {
timer = setTimeout(() => {
fn.apply(context,arg)
timer = null;
},delay)
}
}
}
好啦,总结先到这里,大家有咩有学到一些知识呢,评论区告诉我吧💟