原由:
自己刚在研究js的防抖引发的一个问题(因为不在本篇范围不说了奥),代码:
var count = 1; var container = document.getElementById('container'); function getUserAction() { container.innerHTML = count++; }; function debounce(f, t) { let tid; return function() {
if(tid) {
clearTimeout(tid);
} tid = setTimeout(f, t); } } container.onmousemove = debounce(getUserAction, 3000);
如上所示,基本防抖都有了,但是我想指定getUserAction中的this给debounce,我这个时候就脑缺了...
var count = 1; var container = document.getElementById('container'); function getUserAction() { // this 指向 window
container.innerHTML = count++; }; function debounce(f, t) { let tid; return () => { // this肯定指向 document.getElementById('container')的html元素
if(tid) {
clearTimeout(tid);
}
tid = setTimeout(f.call(this), t);
}
}
container.onmousemove = debounce(getUserAction, 3000);
自己去运行下啊,这时候发现当鼠标移动在container范围中,会失去防抖的功能,也就是说,setTimeOut根本没有执行!!!!(自认为)
其实眼见的同学已经发现了,f.call(this)其实已经执行了该函数,而不是函数声明或者code
根据MDN的解释:
var timerId = setTimeout(func|code, delay)
functionfunction
是你想要在到期时间(delay
毫秒)之后执行的函数。
code这是一个可选语法,你可以使用字符串而不是function
,在delay
毫秒之后编译和执行字符串 (使用该语法是不推荐的, 原因和使用 eval()
一样,有安全风险)。
delay 可选延迟的毫秒数 (一秒等于1000毫秒),函数的调用会在该延迟之后发生。如果省略该参数,delay取默认值0,意味着“马上”执行,或者尽快执行。不管是哪种情况,实际的延迟时间可能会比期待的(delay毫秒数) 值长,原因请查看Reasons for delays longer than specified。
所以,答案很明显了!!!代码改下啊:
var count = 1; var container = document.getElementById('container'); function getUserAction() { // this 指向 window container.innerHTML = count++; }; function debounce(f, t) { let tid; return () => { // this肯定指向 document.getElementById('container')的html元素
if(tid) {
clearTimeout(tid);
}
tid = setTimeout(() => { // 更改为function就行了
f.call(this)
}, t);
}
}
container.onmousemove = debounce(getUserAction, 3000);
那么这个问题解决了啊.好了本文到此结束!!!!
开....开玩笑的奥,是这样的,这时候的确是解决问题了,但我自己想了下,那 tid = setTimeout(f.call(this), t); 到底有没有执行!!!!!
经过,我多方的考证以及最近看了<<你不知道的js>>中了解到,其实在js中存在一个内部的任务队列,用于存储异步任务(setTimeOut,点击事件的回调函数等)
举个栗子:
setTimeout(() => { console.log(1); },0) console.log(2); // result : 2 1
其实,通俗来说同步的任务会按照代码顺序来执行函数,而异步的(当前就是setTimeOut会加入任务队列,即使它的delay为0)
那我们再来个demo,体现有序的队列:
setTimeout(() => { console.log(1); }, 200) setTimeout(() => { console.log(2); }, 100) console.log(3); setTimeout(() => { console.log(4); }, 0) // 根据delay的长短来顺序加入任务队列 console.log(5); // 3 5 4 2 1
说这么多,我就想说明.既然你声明了定时器,那肯定会加入任务队列去执行的.那问题来了,那我的f.call(this)去哪了?众所周知,js中若function没有指定return,默认是返回undefined的,那是不是代表.我的setTimeout(undefined,1000)这样执行了!!!
为了,验证想法,我们可以改下函数的内容 var count = 1;
var container = document.getElementById('container'); function getUserAction() { container.innerHTML = count++; return `alert('done')`; }; function debounce(f, t) { let tid; return () => {
if(tid) {
clearTimeout(tid);
} tid = setTimeout(f.call(this), t);
}
}
container.onmousemove = debounce(getUserAction, 3000);
果然,出来一个alert!!!!!!!那么,到此也就证明了,我们的定时函数还是执行了的~~~~
总结下,setTimeout()第一个参数还是一个function声明,不要直接执行function.或者,在一定需要的时候,去让函数返回这个需要执行的code或function(就和闭包这样的直接return function).
不说了,还是自己太大意了,哈哈哈哈哈~~~~