Javascript: 理解setTimeout中 clearTimeout 使用技巧
2024-12-29 16:14:16
Javascript 函数中使用未来定义的参数
Javascript中经常会用到 setTimeout
函数实现延迟执行的效果,如果延迟期间需要取消并重新计时,clearTimeout
就是必需的工具。这里我们探讨一种看似反直觉的情况:在函数内部,clearTimeout
如何引用一个稍后才会赋值的参数 timeoutID
。
问题剖析
让我们仔细看看这段代码:
let timeoutID;
function updateMessage() {
const message = document.querySelector('.js-message');
message.innerHTML = 'Added';
clearTimeout(timeoutID);
timeoutID = setTimeout(function() {
message.innerHTML = '';
}, 2000);
}
表面上看,clearTimeout(timeoutID)
似乎在使用一个尚未定义的变量 timeoutID
。在 clearTimeout
调用的下一行,timeoutID
才被赋予了 setTimeout
返回的值。这里发生的是 Javascript 中变量作用域和赋值时机的巧妙结合。
作用域和声明提升
首先,let timeoutID;
语句在全局作用域中声明了一个变量 timeoutID
, 但没有给它赋值。关键点是使用 let
声明的变量,在代码块(这里是全局作用域)开始直到执行到变量声明的那行,存在所谓的 “暂时性死区”。 在这里,timeoutID
被声明了,这意味着即使在函数内部访问该变量,也不算“未定义”,Javascript 引擎知道存在这样一个变量,并且能通过该变量找到相应的值。初始值为 undefined 。
函数执行顺序
接下来我们分析代码的执行顺序。每次点击按钮都会调用 updateMessage
函数:
- 更新消息:
message.innerHTML = 'Added';
更新页面消息。 - 取消定时器(尝试):
clearTimeout(timeoutID)
会被调用, 由于此时timeoutID
的值为undefined
,clearTimeout(undefined)
并不会报错,并且相当于没执行。如果之前存在未被清除的定时器,那么本次调用清除的也会是对应的定时器(如果timeoutID
的值已经不是undefined
的情况)。 - 设置新定时器:
timeoutID = setTimeout(...);
会启动一个新的定时器,setTimeout
返回的值(一个唯一标识符,代表该定时器),随后赋值给变量timeoutID
。
这也就意味着在点击按钮第一次时 clearTimeout(undefined)
不会有任何作用,但是会在timeoutID = setTimeout(...)
这一步赋值。 当点击第二次按钮的时候,前一个按钮创建的计时器的id存在 timeoutID
里了, clearTimeout(timeoutID)
可以取消上一个计时器。并且会在此次点击按钮后开启新的计时器,再次把当前新的计时器ID保存进timeoutID
里。
解决方案及原理
理解的关键在于变量的声明和赋值过程,以及 JavaScript 引擎如何处理未初始化变量和函数调用顺序。 代码的关键逻辑利用了 timeoutID
的值可以被更新,以及 clearTimeout(undefined)
并不会导致错误这一点。
总结
这种机制并不是在 “未来” 获取参数。它仅仅是利用了 Javascript 中变量的“声明提升” 和 setTimeout
的返回值会被赋值给 timeoutID
这两点。每次函数调用,都会尝试清理旧定时器,无论之前是否设置过定时器,最终会设置一个新的定时器, 并将id保存。这种方法在很多场景中都非常有用。例如:输入框的搜索自动补全,防止用户频繁输入造成的重复查询请求;按钮的点击事件,避免多次快速点击产生多次请求等场景。
安全建议: 虽然 clearTimeout(undefined)
不会报错,但出于代码清晰度的考虑,可以在clearTimeout
前判断下 timeoutID
是否为真值,例如使用:
if (timeoutID) {clearTimeout(timeoutID)}
这个方式的效率不会差于 clearTimeout(timeoutID)
,也能让代码更易读。
操作步骤:
- 将上面的HTML代码复制粘贴到一个HTML文件中
- 将Javascript代码复制粘贴到html文件里的
<script></script>
标签中。 - 在浏览器中打开该HTML文件,点击 "Add to cart" 按钮进行测试。
- 观察消息 “Added” 出现然后消失的时间间隔,以及多次点击按钮后定时器的行为。
这个例子展示了Javascript中时间延迟函数的高级用法。深入理解背后的机制有助于我们编写更高效,可靠的Javascript代码。