函数防抖与节流
介绍
函数防抖和函数节流是优化高频率执行js代码的一种手段,js中的一些事件如浏览器的resize、scroll,鼠标的mousemove、mouseover,input输入框的keypress等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能。为了优化体验,需要对这类事件进行调用次数的限制。
我们尝试完成一个需求:当鼠标在div上移动时,使它内部的数值不断+1
html
<style>
#box {
width: 100%;
height: 400px;
background-color: royalblue;
font-size: 100px;
color: #fff;
text-align: center;
line-height: 400px;
}
</style>
<div id="box"></div>
<script>
// 定义一个初始值
let num=1;
let oDiv=document.getElementById('box')
// 事件函数
function handle(){
oDiv.innerHTML = num++
console.log(num)
}
oDiv.onmousemove = handle
</script>
作出上面例子我们发现:在移动鼠标的时候,会不断的触发handle函数导致资源浪费,如果此时又需搭配异步请求的话很容易发生阻塞,有没有什么方法能够让事件触发的频率不那么快呢?函数的防抖和节流就是解决这种事件的方案!
防抖
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
以上面的代码为例,我们将js部分改为:
js
let num = 1
let oDiv = document.getElementById('box');
// 事件函数
function handle() {
oDiv.innerHTML = num++
console.log(num);
}
// 防抖函数
function debounce(fn, time) {
// 定义一个定时器
let play;
// 为了确保下面操作的是同一个定时器,这里我们需要使用闭包
return function () {
// 如果这个定时器已经存在就清除它
if (play) clearTimeout(play)
play = setTimeout(function () {
// 这里如果调用的函数不需传参可以直接fn(),如果需要传递参数则需搭配apply方法
fn.apply(this,arguments)
}, time)
}
}
// 鼠标移动停止后1s触发
oDiv.onmousemove = debounce(handle, 1000)
节流
在一段时间内,只会执行一次函数。
同样以上面的代码为例,我们将js部分改为:
js
let num = 1;
var oDiv = document.getElementById('box')
// 事件函数
function handle() {
oDiv.innerHTML = num++;
console.log(num);
}
// 节流函数
function throttle(fn, time) {
let play;
return function () {
// 如果定时器存在,直接返回什么都不做
if (play) return
play = setTimeout(function () {
fn.apply(this,arguments)
// 执行完之后把play设置为null方便下次执行
play = null;
}, time)
}
}
// 鼠标不管怎么移动,触发handle函数的间隔最低为1s
oDiv.onmousemove = throttle(handle, 1000)
上面是由定时器完成的节流,我们也可以使用时间戳的方式实现:
js
let num = 1;
var oDiv = document.getElementById('box')
// 事件函数
function handle() {
oDiv.innerHTML = num++;
console.log(num);
}
// 节流函数
function throttle(fn, time) {
let t=0;
return function(){
let now=new Date();
// 当前时间-上次触发事件的时间>间隔时间时才允许调用函数
if(now-t>time){
fn(this,arguments)
// 将变量t赋值为最后一次触发事件的时间
t=now
}
}
}
oDiv.onmousemove = throttle(handle, 2000)