开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
防抖和节流
防抖和节流的意义
当我们在使用windows的resize,scorll,mousemove,mousehover等方法时,浏览器会默认极高的频率去执行这些方法,这一定程度损失了浏览器的性能,进而影响用户体验,因此我们要对这些方法进行节流处理。
当我们在进行表单提交等与服务器进行交互时,为了防止用户无心的重复触发,所以进行防抖处理。
区别
- 防抖:触发事件后一定时间内无再次触发,则执行事件
- 节流:按规定时间重复执行事件
一、函数防抖
含义:当持续触发事件时,一定时间段内没有触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时,‘函数防抖’的关键在于,在一个动作发生一定时间之后,才会执行特定的事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
#content {
width: 200px;
height: 200px;
line-height: 200px;
background-color: #aaa;
text-align: center;
color: #000;
cursor: pointer;
}
</style>
</head>
<body>
<div id="content"></div>
<script>
/*
连续onmousemove在最后一次触发changeNum函数,
多余的处理函数的都会被clearTimeout掉
*/
let num=1
let oDiv= document.getElementById('content')
let changeNum=function () {
oDiv.innerHTML=num++
}
let deBounce = function (fn,delay){
let timer=null
return function (...args) {
if(timer) clearTimeout(timer)
timer = setTimeout(()=>{
fn(...args)
},delay)
}
}
oDiv.onmousemove=deBounce(changeNum,500)
// or
let _deBounce = deBounce(changeNum,500)
oDiv.onmousemove=function(){
_deBounce()
}
</script>
</body>
</html>
当然,防抖也可以用CSS做,可以参考这篇文章,它是结合了pointer-events、animation、:active来实现的,有兴趣可以了解下。
二、函数节流(throttle)
含义:当持续触发事件时,保证一定时间段内只调用一次事件处理函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<button>点击</button>
</body>
<script>
/*
* 连续点击只会1000执行一次btnClick函数
*/
let obutton = document.getElementsByTagName('button')[0]
// 如果用箭头函数,箭头函数没有arguments,也不能通过apply改变this指向
function btnClick() {
console.log('点击成功')
}
/*
方法1: 定时器方式实现
缺点:第一次触发事件不会立即执行fn,需要等delay间隔过后才会执行
*/
let throttle = (fn, delay) => {
let flag = false
return function(...args) {
if (flag) return
flag = true
setTimeout(() => {
fn(...args)
flag = false
}, delay)
}
}
/*
方法2:时间戳方式实现
缺点:最后一次触发回调与前一次的触发回调的时间差小于delay,则最后一次触发事件不会执行回调
*/
let throttle = (fn, delay) => {
let _start = Date.now()
return function(...args) {
let _now = Date.now(),
that = this
if (_now - _start > delay) {
fn.apply(that, args)
start = Date.now()
}
}
}
// 方法3:时间戳与定时器结合
let throttle = (fn, delay) => {
let _start = Date.now()
return function(...args) {
let _now = Date.now(),
that = this,
remainTime = delay - (_now - _start)
if (remainTime <= 0) {
fn.apply(that, args)
} else {
setTimeout(() => {
fn.apply(that, args)
}, remainTime)
}
}
}
/*
方法4:requestAnimationFrame实现
优点:由系统决定回调函数的执行机制,60Hz的刷新频率,每次刷新都会执行一次回调函数,不
会引起丢帧和卡顿
缺点:1.有兼容性问题2.时间间隔有系统决定
*/
let throttle = (fn, delay) => {
let flag
return function(...args) {
if (!flag) {
requestAnimationFrame(function() {
fn.apply(that, args)
flag = false
})
}
flag = true
}
}
obutton.onclick = throttle(btnClick, 1000)
</script>
</html>