防抖函数的实现
第一步,知道防抖是什么,哪些应用场景。 比如,电梯门的开关,如果一个人要通过了,这个时候电梯在5s内要关闭,如果又来了一个人进去了,电梯会重新开始计时。
我们一般在搜索框输入值,页面的滚动时,页面改变的大小统计时,会触发防抖函数。
第二步,能够手写防抖
如下,此时点击按钮,就会立马触发函数
<button id="pay">点击付钱</button>
<script>
let button = document.querySelector('#pay')
function payMoney() {
console.log('已经剁手付钱了');
}
button.addEventListener('click', payMoney)
</script>
第三步,
这里的效果是,页面一刷新,还没有点击,就被调用了。
困惑,为什么一刷新,而不是点击,才被调用呢?因为这里的,debounce()函数是直接执行,里面的func也是直接执行
<button id="pay">点击付钱</button>
<script>
let button = document.querySelector('#pay')
function payMoney() {
console.log('已经剁手付钱了');
}
+ function debounce(func) {
+ func()
+ }
button.addEventListener('click', debounce(payMoney))
补充知识点,addEventListener的第三个参数是一个options可选的对象:
capture:布尔值,表示回调函数会在该类型的事件捕获阶段传递到eventTarget时才触发
once: 如果为true,只执行一次
第四步,返回一个函数,就能够实现,点击才触发函数
let button = document.querySelector('#pay')
function payMoney() {
console.log('已经剁手付钱了');
}
function debounce(func) {
+ return function () {
func()
}
}
button.addEventListener('click', debounce(payMoney))
第五步,添加时间延迟
尽管已经有了时间延迟,但是每个函数只是延迟触发,并没有时间函数执行的次数限制
let button = document.querySelector('#pay')
function payMoney() {
console.log('已经剁手付钱了');
}
function debounce(func, delay) {
return function () {
setTimeout(function () {
func()
+ }, delay)
}
}
+ button.addEventListener('click', debounce(payMoney, 1000))
第六步,给定时器设置变量名,在前面清除定时器,实现函数执行次数的限制,
let button = document.querySelector('#pay')
function payMoney() {
console.log('已经剁手付钱了');
}
function debounce(func, delay) {
return function () {
+ clearTimeout(timer)
+ timer = setTimeout(function () {
func()
}, delay)
}
}
第七步,上面这样执行会报错,因为不能在定义变量之前,使用变量,“let”关键字
不能这样定义,不然,还是无法限制函数执行的次数。创建了很多个函数块,里面的每个函数块都会有自己的作用域,
let button = document.querySelector('#pay')
function payMoney() {
console.log('已经剁手付钱了');
}
function debounce(func, delay) {
return function () {
+ let timer;
clearTimeout(timer)
timer = setTimeout(function () {
func()
}, delay)
}
}
修改如下,这里利用了作用域链的机制,是一个闭包的实现,能够实现延迟执行的效果
let button = document.querySelector('#pay')
function payMoney() {
console.log('已经剁手付钱了');
}
function debounce(func, delay) {
+ let timer
return function () {
clearTimeout(timer)
timer = setTimeout(function () {
func()
}, delay)
}
}
第八步,我们关注this的指向,
this如下指向的window,但是我们希望,按钮点击,this指向的是按钮本身。但是为什么下面的this指向的window,因为回调函数的原因,他触发的时候,已经在windows下面的了。
let button = document.querySelector('#pay')
function payMoney() {
+ console.log(this); // window
console.log('已经剁手付钱了');
}
function debounce(func, delay) {
let timer
return function () {
clearTimeout(timer)
timer = setTimeout(function () {
func()
}, delay)
}
}
button.addEventListener('click', debounce(payMoney, 1000))
我们可以在函数执行时,提前保存好this
并且使用apply修改this的指向,
let button = document.querySelector('#pay')
function payMoney() {
console.log(this); // window
console.log('已经剁手付钱了');
}
function debounce(func, delay) {
let timer
// 有没有想过 this的赋值为啥不写这里?因为这里是debounce的环境,必定是this指向window
return function () {
clearTimeout(timer)
+ let context = this
timer = setTimeout(function () {
+ func.apply(context)
}, delay)
}
}
button.addEventListener('click', debounce(payMoney, 1000))
第九步,我们考虑函数参数的情况
function debounce(func, delay) {
let timer
// console.log(this); // 这里指向的是window 为什么给button绑定的事件 this指向的是window呢
return function () {
clearTimeout(timer)
let context = this
+ let args = arguments
timer = setTimeout(function () {
+ func.apply(context, args)
}, delay)
}
}
\