节流函数引发的一连串疑问

162 阅读2分钟

最简单的节流函数

function debounce(fn, wait=500) {
    let timer = null
    return function() {
        if(timer) 
            clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, wait);
    }
}

问题1:为什么要fn.apply(this)

什么场景会涉及到this指向问题?

比如浏览器的事件监听,回调函数进行节流处理,回调函数内部的this应该指向事件监听的对象。

问题2:debounce函数内部,this?

// 事件回调节流场景下 callback = debounce(fn),this指向如下:
function debounce(fn, wait=500) {
     // this指向window(严格模式指向undefined)(debounce在全局环境调用)
    let timer = null
    return function() {
        // this指向fn的this(事件监听的对象)
        if(timer) 
            clearTimeout(timer)
        timer = setTimeout(() => {
            // this指向fn的this
            fn.apply(this, arguments)
        }, wait);
    }
}

问题3:setTimeout回调不用箭头函数?

如果setTimeout回调不用箭头函数,this指向window。严格模式下呢?

问题4:严格模式下,setTimeout回调内部this也指向window?

setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。 即使是在严格模式下,setTimeout()的回调函数里面的this仍然默认指向window对象, 并不是undefined。 —— MDN

developer.mozilla.org/zh-CN/docs/…

立即执行的节流函数

function debounce(fn, wait=500, immediate=false) {
    let timer = null
    return function() {
        if(timer) 
            clearTimeout(timer)
        if(immediate) 
        	!timer && fn.apply(this,arguments)
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, wait);
    }
}

如果fn函数不抛异常,节流可以正常实现。如果fn抛异常,timer=xx.. 不会执行,节流失败。

问题1:如何避免fn异常后节流失败?

function debounce(fn, wait=500, immediate=false) {
	let timer = null;
	return function() {
		if(timer)
			clearTimeout(timer);
		if(immediate) {
			let _timer = timer; // 保存timer,将fn函数的执行放到节流计时器之后执行。
			timer = setTimeout(() => (timer=null), wait);
			if(!_timer)
				fn(this, arguments);
		}else{
			timer = setTimeout(() => {
				fn.apply(this, arguments);
			}, wait);
		}
	}
}

在一些文章中看到,节流应该附加考虑一下,是否支持取消?

为什么要取消呢?不理解。由此衍生出下一个问题。

问题2:setTimeout不取消会咋的?

为了获取专业的答案,翻墙google,最后在stackoverflow找到了答案。

If handle does not identify an entry in the list of active timers of the WindowOrWorkerGlobalScope object on which [clearTimeout] was invoked, the method does nothing.

html.spec.whatwg.org/multipage/t…

意思就是,setTimeout不是必须要clearTimeout去清理计时器的。clearTimeout API的设计,只是为了在你想要清理计时器时使用。不过setInterval大多数情况下都需要使用clearInterval清理。

关于节流函数,应该没有什么疑问了吧?