2011-08-06 14 views
1

私は最近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

'setTimeout'ではなく' setInterval'が必要です。 – FK82

+0

@ fk82彼はstartをもう一度呼び出すので、このシナリオではsetTimeoutは問題ありません。 –

+0

@Gaby:あいまいです。コードはそれを行います。実際には私の意図は

の(...)内容は毎秒1ずつインクリメントされるべきだと思っています。 ":-)ユーザが毎秒正確にクリックしない限り、起こらないのはどれですか? – FK82

答えて

2

この問題(または私が最初に見た最初のもの)は、文字列 "increment(obj)"をsetTimeout()メソッドに渡していますが、objstart()メソッド内でのみ定義されています。渡す文字列は、タイムアウトトリガーまで実際には評価されません。その時点で変数objは有効範囲にありません。

これを回避する方法はいくつかあります。一つは次のように、代わりにJavaScriptの文字列のsetTimeout()にクロージャを渡すことである。

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) 

あなたはいつも、このような011などの値との誤差を避けるために、parseIntに基数の引数を提供しなければならない(むしろ11より、9である、それ理由先頭の0のためにベース8で評価されます)。

parseInt(obj.innerHTML, 10) 

...ベース10の解析を強制することで、これらの欠点を回避できます。とにかく

、ここでの例作業:あなたはsetTimeoutに渡す文字列はグローバルスコープではなく、関数内のスコープで評価され、かつので、あなたのobjオブジェクトを参照していないのでhttp://jsfiddle.net/dSLZG/1

5

を。

文字列をsetTimeoutに渡すべきではありません。代わりに、それを関数の参照渡し:

function start(obj) 
{ 
    var t = setTimeout(function() { 
       increment(obj); 
      }, 1000); 
} 

function increment(obj) 
{ 
    obj.innerHTML = parseInt(obj.innerHTML) + 1; 
    start(obj); 
} 

を我々はsetTimeoutに渡している機能は、それが定義されていますスコープ内のアイテムへの永続的な参照を持って閉鎖手段、です。そのため、タイマ機構がそれを呼び出す2番目の後には、start関数が返されてからも、start関数の引数objにアクセスできます。もっとここに:Closures are not complicated

1

問題のコード行である:

objは、関数のスコープ内の識別子である - それはstart関数内でのみアクセス可能です。文字列をsetTimeoutに渡すと、グローバルスコープで評価されます。つまり、obj変数は使用できないため、何も増えません。

これはクロージャを作成し、あなたの変数がアクセス可能になりますようあなたは、代わりに関数オブジェクトを渡す必要があります:あなたが何もしていないので、私は、不要なvar t =を削除した

function start(obj) 
{ 
    setTimeout(function() { 
     increment(obj); 
    }, 1000); 
} 

注意をタイマー識別子。

0

コードをjsFidleにコピーし、実際の例を追加しました。 sample codeを参照してください。 元のバージョンの問題は、変数objがグローバルコンテキストで定義されていないことです。文字列をsetTimeoutに渡すと、グローバルコンテキストで文字列が評価されることになります(objは変数ではありません)。

代わりに行うべきことは、文字列をsetTimeoutではなく関数オブジェクトに渡すことです。この関数オブジェクトは、関数オブジェクトが定義された場所に設定されたすべての変数への参照を保持します。
に渡された関数オブジェクトがstart2に定義されているため、start2に定義されているため、すべての変数とパラメータには、start2というアクセス権があります。変数objはその1つで、setTimeoutによって実行されるとすぐに関数オブジェクト内でアクセス可能です。

関連する問題