为什么我的计时器不会增加数字?

时间:2011-08-06 13:04:35

标签: javascript

我最近学习了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。有谁知道为什么这不起作用?

4 个答案:

答案 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执行它就可以在函数对象中访问。