我们在实际开发当中,可定会遇见持续触发的事件如:onmousemove
,onchange
,resize
等这种持续触发的事件,我们还可能会遇见那么持续需要点击的功能:抽奖
,点击购买商品
等功能
以上这种情况都会引出来一个问题,那就是页面可能会卡顿,性能较低等问题
那么我们可以通过函数节流和函数防抖等我们人为定义函数功能的方式来解决这种问题
举个栗子,当 1 秒内连续播放 24 张以上的图片时,在人眼的视觉中就会形成一个连贯的动画,所以在电影的播放中基本是以每秒 24 张的速度播放的,为什么不 100 张或更多是因为 24 张就可以满足人类视觉需求的时候,100 张就会显得很浪费资源
再举个栗子,假设电梯一次只能载一人的话,10 个人要上楼的话电梯就得走 10 次,是一种浪费资源的行为;而实际生活正显然不是这样的,当电梯里有人准备上楼的时候如果外面又有人按电梯的话,电梯会再次打开直到满载位置,从电梯的角度来说,这时一种节约资源的行为(相对于一次只能载一个人)。
函数节流的核心是,让一个函数不要执行得太频繁,减少一些过快的调用来节流。
函数去抖就是对于一定时间段的连续的函数调用,只让其执行一次。
throttle
)应用场景函数节流有哪些应用场景?哪些时候我们需要间隔一定时间触发回调来控制函数调用频率?
mousemove
)mousedown
/keydown
事件(单位时间只能发射一颗子弹)mousemove
)mousemove
)keyup
)debounce
)应用场景函数去抖有哪些应用场景?哪些时候对于连续的事件响应我们只需要执行一次回调?
resize
/scroll
触发统计事件函数节流和函数防抖的核心其实就是限制某一个方法被频繁触发,而一个方法之所以会被频繁触发,大多数情况下是因为 DOM 事件的监听回调,而这也是函数节流以及防抖多数情况下的应用场景。
<script>
/*
* 函数防抖
* 在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。
**/
// technique防抖函数:接收的形参 fn函数 time时间
function technique(fn, time) {
let resultTime = null // 定时器的返回值(用于清除定时器)
return function (...args) { // 返回一个函数并且扩展出一些参数
clearTimeout(resultTime) // 清除定时器
// 执行定时器 并且该变fn函数的this指向和设置time的时间
resultTime = setTimeout(fn.bind(this, ...args), time)
}
}
// 使用
const resultFn = technique(function (e) {
console.log('执行', this, e);
}, 1000)
document.addEventListener('click', resultFn)
</script>
代码解读
technique
的函数,并且接收两个参数(函数(fn)
与执行的时间(time)
)resultTime
来接收我们定时器返回出来的值,用于我们清除上一次定时器return
返回出来一个函数并且用扩展运算符来扩展出来一下参数(如事件对象
,我们传递过来的参数
)technique
函数的时候需要清除一下我们上一次开启的定时器,并且在开启一个定时器,还需要用bind
改变fn
函数的this
指向问题和传递参数的问题technique
防抖函数了<script>
/*
* 函数节流
* 连续执行函数,每隔一定时间执行函数。规定一个单位时间,
* 在这个单位时间内,只能有一次触发事件的回调函数执行,
* 如果在同一个单位时间内某事件被触发多次,只有一次能生效。
* */
// throttle节流函数 接收的参数:fn函数,time时间
function throttle(fn, time) {
let start = 0 // 初始函数节流的时间
return function (...args) { // 返回一个函数并且扩展出一些参数
const newDate = new Date() // 获取当前最新的时间
// 如果最新的时间-节流的时间 >= time我们人为定义节流的时间 那么就执行
if (newDate - start >= time) {
start = new Date() // 从新设置初始节流的时间
fn.call(this, ...args) // 修改this指向并且传递一些参数
}
}
}
// 应用
const resultFn = throttle(function (e) {
console.log('执行', this, e);
}, 1000)
document.addEventListener('click', resultFn)
</script>
代码解读
throttle
的函数,并且接收两个参数(函数(fn)
与执行的时间(time)
)start
变量,用于记录初始的节流时间默认是0
即可...args
,而在这个函数内部我们在定义一个当前最新的时间变量newDate
if
的判断:如果最新的时间(newDate)-节流的时间(start) >= 我们人为定义节流的时间(time)
,那么我们就从新设置节流的时间start
,并且改变fn
函数的this
执行和传递一些参数throttle
节流函数了css+html
代码
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.box {
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 200px;
background-color: pink;
cursor: pointer;
}
style>
<div class="box">div>
javascript
代码
var oBox = document.getElementsByClassName("box")[0]
// 点鼠标点击盒子的时候 触发mousedown事件
oBox.addEventListener('mousedown', function (e) {
// 并且获取他的clientX和Y值
var clientX = e.clientX
var clientY = e.clientY
// 并且获取他的offsetTop和offsetLeft值
var offsetTop = this.offsetTop
var offsetLeft = this.offsetLeft
// 当鼠标移动的时候触发mousemove事件
function move(e) {
// 并且获取他的clientY和X值
var moveX = e.clientX
var moveY = e.clientY
// 并且改变他的left和top值
oBox.style.left = offsetLeft + moveX - clientX + 'px'
oBox.style.top = offsetTop + moveY - clientY + 'px'
}
// mousemove事件 函数节流 并且传递move函数
function throttle (fn, time = 100) {
var start = 0; // 初始节流的时间
return function (...args) {
var now = new Date
if (now - start >= time) {
fn.call(this, ...args)
start = now
}
}
}
var throttleFn = throttle(move, 80)
// 把函数节流的函数作为mousemove的事件处理函数
document.addEventListener('mousemove', throttleFn)
// 当鼠标抬起的时候 清除事件
oBox.addEventListener('mouseup', function () {
document.removeEventListener('mousemove', throttleFn)
oBox.removeEventListener('mouseup', oBox)
})
})
上面代码我们要实现一个
div
盒子拖动的效果,我们用到了onmousemove
时间,而这个事件是多次触发的一个事件,我们可以用节流函数来优化他,并且我们人为可以设置他的拖拽时间
react-hooks
代码
// 当触发input框当中的change事件的时候触发该事件处理函数
const handleChange = e => {
const val = e.target.value
const result = technique(function () {
setVal(val) // 设置对应的val值
getQueryData(val) // 请求对应的数据
if (!val) { // 如果val值为空那么我们就发送一个[]数组 代表没有获取到数据
Pubsub.publish('SongList', [])
}
}, 20)
result()
}
technique
防抖函数
// 防抖函数
export function technique (fn, time = 1000) {
let resultTime = null
return function (...args) {
clearTimeout(resultTime)
resultTime = setTimeout(fn.bind(this, ...args), time)
}
}
这段代码是我在一个
react hook
项目上面拿到的一段代码,大概的功能是我们输入input
框的时候需要使用onchange
来实时监测他的value
值的变化,并且想后台发起请求来获取对应的歌曲数据,在这个功能上面我们使用了防抖函数来优化他,让他在我们认为定义的时间内去获取歌曲数据的请求
小结
节流和防抖函数主要适用于高频触发的操作,如果对于高频触发的时间我们不加以限制,会导致频繁的页面渲染和或者请求等。影响用户体验,此时我们可以用
debounce()
和throttle()
的方式来减少回调被处罚的频率,这样既可以达到我们想要的效果又可以减弱对用户体验的影响。