今天心血来潮重写防抖节流,写着写着突然冒出一个问题,为什么执行函数的时候要用apply呢,为什么不直接写fn(...args)呢?
js
//防抖
function debounce(fn, wait, immediate = false) {
let timer = null;
return function (...args) {
if (immediate && timer === null) {
fn.apply(this, args);
}
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (!immediate) {
fn.apply(this, args); //为什么要用apply?
}
}, wait)
}
}
let a = 1;
function onClick() {
console.log(this)
console.log(a++)
}
document.addEventListener('click', debounce(onClick, 1000, true))
虽然关于this的指向也背的七七八八,但不得不说,在真实的开发中,有时候还是会被绕进去。那么今天就来扫荡一下迷雾吧。
首先,在这个例子当中,给document绑定了一个click事件,事件处理函数是debounce(onClick, 1000, true),也就是防抖函数这个高阶函数的返回值。
如果这里没有防抖,而是直接这样写:
js
document.addEventListener('click', debounce(onClick, 1000, true))
那么onClick内部的this会指向谁呢?答案是指向document。因为浏览器的事件绑定机制是,在哪个元素上绑定了事件函数,该函数的this就指向这个元素。相当于浏览器执行了onClick.call(document, event)。
经过debounce包装了onClick后,它内部的this应该与没包装的时候一样,指向document。
但是,现在onClick与document之间的直接联系已经被切断了,相当于之前是手拉手,现在中间硬插了一个人进来:
js
// document--onClick 之前
// document--debounce--onClick 现在
于是,我们就只能通过插入的这个人来进行一个传递。现在,事件处理函数从onClick变成了debounce(onClick, 1000, true),而后者就等同于debounce函数返回的那个函数,因此这个返回函数的this指向了document:

所以现在事情就好办了,只需要在这个返回函数里面调用fn的时候,把this指向传递进去就可以了。可以用apply,也可以用call。而如果不这样做的话,就会出现下图的情况,this没有正确传递:

