使用函数节流与函数防抖的目的,就是为了节约计算机资源,提升用户体验。js中的一些事件如浏览器的resize、scroll,鼠标的mousemove、mouseover,input输入框的keypress等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能。为了优化体验,需要对这类事件进行调用次数的限制。
防抖:短时间内大量触发,但只执行一次。原理:设置定时器,delay时间后执行事件处理,期间每次触发事件都会将定时器重置,直到delay时间内无第二次事件触发。
function debounce (func, delay) {
let task = null
return function (...args) {
if (task) {
clearTimeout(task)
}
task = setTimeout(() => {
func.apply(this, ...args)
}, delay)
}
}
// 使用
let debounceFunc = debounce(scrollEvent, 300)
document.getElementById("my_input").addEventListener("keydown", function () {
debounceFunc(456)
})
function scrollEvent(args) {
console.log("滚动" + args);//滚动456
}
节流:隔一段时间执行一次,期间被多次触发也不管。实现原理:设置定时器,delay时间后执行事件处理,当事件执行完后清除定时器。
function throttle (func, delay) {
let task = null
return function (...args) {
if (!task) {
task = setTimeout (() => {
task = null
func.apply(this, ...args)
}, delay)
}
}
}
// 使用
let throttleFunc = throttle(scrollEvent, 300)
document.addEventListener("scroll", function () {
throttleFunc(123)
})
function scrollEvent(args) {
console.log("滚动" + args);
}
对于两函数中的func.apply(this, ...args),之前感觉一直没弄清楚,于是查了很多资料,以下是自己的分析。
知识点详解:
1.定时器回调函数中的this指向问题:
(1)如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。这是因为JS的定时器方法是定义在window下的。
(2)call,apply,bind可以修改this指向
(3)箭头函数没有自己的this,它的this继承自外部函数的作用域。
因此上面代码setTimeout中的this继承外层匿名函数的this,也就是节流和防抖函数的调用者。
2.apply方法的使用
Function.apply(obj,args)可将函数的this对象指向obj,args则为函数的输入参数,以数组形式传入。
上面代码中func.apply(this, ...args)则是将func的this指向改为setTimeout中的this,也就是将func的this指向改为节流和防抖函数的调用者。
上文所写的节流和防抖函数的返回值是一个函数,因此在调用时可以给其传参,输入参数可以使用剩余参数语法function (...args)写入返回的匿名函数中,并利用func.apply(this, ...args)的第二个参数传给func。