我最近学习了javascript。我正在试验它。现在,我试着制作一个简单的计时器。这是代码:
<html>
<head>
<script type="text/javascript">
function start(obj)
{
var t = setTimeout("increment(obj)", 1000);
}
function increment(obj)
{
obj.innerHTML = parseInt(obj.innerHTML) + 1;
start(obj);
}
</script>
</head>
<body>
<p onclick="start(this)">0</p>
</body>
</html>
<p></p>
的内容应该每秒递增1。有谁知道为什么这不起作用?
答案 0 :(得分:5)
因为传递给setTimeout
的字符串是在全局范围内计算的,而不是函数中的范围,因此不会引用您的obj
对象。< / p>
您永远不应将字符串传递给setTimeout
。相反,传递一个函数引用:
function start(obj)
{
var t = setTimeout(function() {
increment(obj);
}, 1000);
}
function increment(obj)
{
obj.innerHTML = parseInt(obj.innerHTML) + 1;
start(obj);
}
我们传递给setTimeout
的函数是一个闭包,这意味着它对于定义它的作用域中的项具有持久的引用。因此,当计时器机制调用它时,它仍然可以访问obj
函数的start
参数,即使start
函数早已返回。更多信息:Closures are not complicated
答案 1 :(得分:2)
问题(或者至少是我看到的第一个)是你将字符串“increment(obj)”传递给setTimeout()
方法,但obj
仅在你的内部定义start()
方法。在超时触发之前,实际上不会评估您传递的字符串,此时没有obj
变量在范围内。
有几种不同的方法可以解决这个问题。一种是将闭包传递给setTimeout()
而不是JavaScript字符串,例如:
function start(obj) {
var nextIncrement = function() {
increment(obj);
};
var t = setTimeout(nextIncrement, 1000);
}
另一个(虽然不太可取)选项是将obj
提升到全局范围,例如:
function start(obj) {
window.obj = obj;
var t = setTimeout("increment(obj)", 1000);
}
但是,一般情况下,应避免将字符串传递给setTimeout
(并且还应避免不必要地将事物放在全局范围内)。正如您所看到的,它可能会导致范围分辨率问题,并且对于任何非平凡的操作,它也会使您的代码难以维护。总是喜欢在可能的情况下传递函数闭包。
此外,以下代码行是probably not doing exactly what you expect:
parseInt(obj.innerHTML)
你应该始终为parseInt
提供基数参数,以避免出现011
等值的错误(这是9,而不是11,因为它是在基数为8时由于领先而被评估0
)。您只需执行以下操作即可避免这些怪癖:
parseInt(obj.innerHTML, 10)
...强制进行10-base解析。
无论如何,这里的工作示例:http://jsfiddle.net/dSLZG/1
答案 2 :(得分:1)
问题在于这行代码:
var t = setTimeout("increment(obj)", 1000);
obj
是函数范围中的标识符 - 只能在start
函数中访问。将字符串传递给setTimeout
时,将在全局范围内对其进行求值。这意味着obj
变量不可用,因此不会增加任何内容。
您应该传递一个函数对象,因为这将创建一个闭包,您的变量将是可访问的:
function start(obj)
{
setTimeout(function() {
increment(obj);
}, 1000);
}
请注意,我已删除了不必要的var t =
,因为您没有对计时器标识符执行任何操作。
答案 3 :(得分:0)
我已将代码复制到jsFidle并添加了一个工作示例。见sample code。
原始版本中的问题是您的变量obj未在全局上下文中定义。每当您将字符串传递给setTimeout
时,您最终都会在全局上下文中评估字符串(其中obj
不是变量)。
你应该做的是永远不会将字符串传递给setTimeout
而是一个函数对象。此函数对象保存对定义函数对象时设置的所有变量的引用
因此,因为setTimeout
中传递给start2
的函数对象是在start2
中定义的,所以它可以访问start2
的所有变量和参数。您的变量obj
就是其中之一,因此只要setTimeout
执行它就可以在函数对象中访问。