a=1;
function hoge(){
var a=2;
setTimeout("alert(a)",1000);
setTimeout(function(){alert(a)},2000);
}
hoge();
上の結果が「1」「2」となるのは何故なんでしょうか?
まず、関数の外と中で定義している変数aが別のものになります。(関数内でvarで宣言されてるため)
最初のalertは、グローバルとして動くalertなので、関数の外のaを参照します。
二つめのalertではhoge関数内で働くため、関数内のaを参照します。
ということではないでしょうか。
ちなみに、試されてるかもしれませんが、関数内でvarを取ると、グローバルのaを書き換えるので、どちらも2になります。
イメージ的にはこういう感じではないでしょうか?
a=1;
function kansu(s){
alert(s);
}
function hoge(){
var b=2;
setTimeout("kansu(a)",1000);
setTimeout(function(){alert(b)},2000);
}
hoge();
なるほど。
1つ目の関数はダブルクォートで囲まれているので、実行時にaの変数を参照しますが、
2つ目は、宣言時にaの値を渡しています。
1つ目の関数も、
setTimeout("alert(" + a + ")",1000);
とすれば、「2」が表示されるかと思います。
でも、
eval("alert(a)");
は「2」で返ってくるんです。
これは、もう、体系的に理解しようというのが間違っていて、setTimeoutの仕様として考えるしかないのでしょうか? 何故にそんな混乱しそうな仕様にしたのでしょうか。
結論から言うと、仕様として考えるしかないと思います。
コードが文字列で指定されているか否かでスコープが決まるわけではなく、それはあくまで各関数の仕様によります。
また、
http://www2u.biglobe.ne.jp/~oz-07ams/prog/js-notes/setTimeout.ht...
にも書かれている通り、IEとFirefoxとでもその動作は微妙に違います。
当初、文字列指定のみで関数指定はできなかったようですから、その際スコープをグローバルにしたのでしょう。(あくまで想像ですが。) クロージャのレキシカルスコープは分かりにくい部分もありますし・・・。
ただ、確かに混乱しますね。個人的には、文字列指定は使わずに関数指定だけを使っています。
有難うございます。
だめだ。リンク先を見ている段階で混乱してきました。必要のないときは変数で渡さないようにしようと思いました。
a=1; function hoge(){ var a=2; setTimeout("alert(a)",1000); setTimeout(function(){alert(a)},2000); } hoge();
は、
a=1; function hoge(){ var a=2; function huga() { alert(a); } setTimeout("alert(a)",1000); setTimeout(huga,2000); } hoge();
と同じです。(正確には2つの setTimeout の間に fuga() の宣言を書くべきですが無視)
挙動を"感覚"できるように、
a=1; function hoge(){ var a=2; function huga() { a = a + 1; alert(a); } setTimeout("alert(a)",1000); setTimeout(huga,2000); setTimeout(huga,3000); } hoge();
としてみましょう。
結果は 1,3,4 と出てきます。
hoge の中で var a で確保された変数が、fuga の実行ごとに1増えています。
もう setTimeout("alert(a)",1000); は無視して話をしますよ。
a=1; function hoge(){ var a=2; function huga() { a = a + 1; alert(a); } setTimeout(huga,2000); setTimeout(huga,3000); } setTimeout(hoge, 1000); setTimeout(hoge, 10000);
とすると 3,4 と出て、ちょっと(7秒ほど)してまた 3,4 とでてきます。
の順に実行されてます。
hogeの実行の度に(グローバルじゃない)変数aの領域が確保され、それがhogeに束縛されている、と考えればいいでしょう。
つまり、"1回目のhogeに束縛されたa"と"2回目のhogeに束縛されたa"は別であるという概念で捉えてみて、それが正しいかどうかを確認するためのソースを書いてみます。
b=1; function hoge(){ var a=b; function huga() { a = a + 1; alert(a); } setTimeout("b=b+10",1000); setTimeout(huga,2000); setTimeout(huga,10000); //※ } setTimeout(hoge, 1000); setTimeout(hoge, 5000); setTimeout("alert(b)", 20000);
※の行が遅延して実行される様に時間設定しています。
こういう風に実行されるだと考えられます。
実行すると確かに 2,12,3,13,21 の順に表示されます。
解答、解説じゃなくてすみません。
質問が興味深かったので色々と実験してみた結果でした。
えっと、難しいです。これは何かのイジメですか(w
ところで、最後のなんですが。
b=1;
function hoge(){
var a=b;
function huga() {
a = a + 1;
alert(a);
}
setTimeout("b=b+10",1000);
setTimeout(huga,10000); //※
setTimeout(huga,2000);
}
setTimeout(hoge, 5000);
setTimeout(hoge, 1000);
setTimeout("alert(b)", 20000)
これでも実行結果同じなんですね。これは危険が危ない。
var a = 1;
function hoge1() {
var a = 2;
var f = function(){alert(a)};
// var f = new Function("alert(a)");
hoge2(f);
var a = 3;
}
function hoge2(f) {
var a = 4;
alert(f);
f();
setTimeout(f, 1000);
}
hoge1();
関数リテラルは、作成されたときのスコープが引継がれるようです。
http://www.atmarkit.co.jp/fdotnet/ajaxjs/ajaxjs02/ajaxjs02_02.ht...
この連載の次回に期待
ああ、なるほど。
これは分かりやすいですね。有難うございます。
なるほど。
やっと疑問点が理解できました。
「setTimeout()関数は、どうして常にローカル変数aを参照しないのか?」
という疑問だったのですね。
答えは「最初から関数内の変数は見ていない」です。
setTimeout()には引数としてオブジェクトを渡します。
setTimeout("alert(a)",1000);
とされた場合、
タイマー(仮の呼び名)には、
"alert(a)"
という文字列オブジェクトが渡されます。
それに対して
var a=2;
setTimeout(function(){alert(a)},2000);
は、
function(){alert(2)}
という関数オブジェクトが渡されます。
(aは変数なので、呼ばれた時点で値が評価される)
"alert(" + a + ")"
も同様で、aの値は呼ばれたときに評価され、
"alert(2)"
の文字列オブジェクトが渡されます。
したがって、タイムアウト時に実行される関数は
タイマー(仮名)が確保している、
alert(a);
と
alert(2);
の2つのパターンとなります。
どちらも、この時点では呼び出し元のaの事はすっかり忘れています。
もともと意識していないというのが正解です。 そもそもローカル変数は、呼び出し元の関数と一緒に解放されて、 実行時にはすでに参照できなくなっている事でしょう。
つまり、大雑把に言えば、setTimeout()の仕事は、引数のオブジェクトをタイマー(仮名)に渡すことだけなのです。
なるほど。
大きく理解が進んだような気がします。
しかし、自分レベルのスクリプトだとバグを出してしまいそうな動作で、怪しい時にはsetTimeoutには変数を使わずリテラルで渡そうと決意しました。
有難うございます。
最初のalertと2つ目のalertが何故スコープが変わるのか知りたいです。引き続きよろしくお願いします。
setTimeout(alert(a),2000);
こちらでも、エラーが出ているのですが「2」になるのです。