javascriptに関する質問です。


a=1;
function hoge(){
var a=2;
setTimeout("alert(a)",1000);
setTimeout(function(){alert(a)},2000);
}
hoge();

上の結果が「1」「2」となるのは何故なんでしょうか?

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2007/07/18 10:48:59
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答7件)

id:jippu No.1

回答回数61ベストアンサー獲得回数4

ポイント25pt

まず、関数の外と中で定義している変数aが別のものになります。(関数内でvarで宣言されてるため)

最初のalertは、グローバルとして動くalertなので、関数の外のaを参照します。

二つめのalertではhoge関数内で働くため、関数内のaを参照します。

ということではないでしょうか。

ちなみに、試されてるかもしれませんが、関数内でvarを取ると、グローバルのaを書き換えるので、どちらも2になります。

id:Lhankor_Mhy

有難うございます。

最初のalertと2つ目のalertが何故スコープが変わるのか知りたいです。引き続きよろしくお願いします。


setTimeout(alert(a),2000);

こちらでも、エラーが出ているのですが「2」になるのです。

2007/07/16 19:38:07
id:jippu No.2

回答回数61ベストアンサー獲得回数4

ポイント25pt

イメージ的にはこういう感じではないでしょうか?

a=1;

function kansu(s){

alert(s);

}

function hoge(){

var b=2;

setTimeout("kansu(a)",1000);

setTimeout(function(){alert(b)},2000);

}

hoge();

id:Lhankor_Mhy

なるほど。

2007/07/16 20:56:47
id:suenaga3 No.3

回答回数20ベストアンサー獲得回数6

ポイント25pt

1つ目の関数はダブルクォートで囲まれているので、実行時にaの変数を参照しますが、

2つ目は、宣言時にaの値を渡しています。

1つ目の関数も、

setTimeout("alert(" + a + ")",1000);

とすれば、「2」が表示されるかと思います。

id:Lhankor_Mhy

でも、


eval("alert(a)");


は「2」で返ってくるんです。

これは、もう、体系的に理解しようというのが間違っていて、setTimeoutの仕様として考えるしかないのでしょうか? 何故にそんな混乱しそうな仕様にしたのでしょうか。

2007/07/16 21:00:45
id:susie-t No.4

回答回数99ベストアンサー獲得回数18

ポイント150pt

結論から言うと、仕様として考えるしかないと思います。

コードが文字列で指定されているか否かでスコープが決まるわけではなく、それはあくまで各関数の仕様によります。

また、

http://www2u.biglobe.ne.jp/~oz-07ams/prog/js-notes/setTimeout.ht...

にも書かれている通り、IEとFirefoxとでもその動作は微妙に違います。

当初、文字列指定のみで関数指定はできなかったようですから、その際スコープをグローバルにしたのでしょう。(あくまで想像ですが。) クロージャのレキシカルスコープは分かりにくい部分もありますし・・・。

ただ、確かに混乱しますね。個人的には、文字列指定は使わずに関数指定だけを使っています。

id:Lhankor_Mhy

有難うございます。


だめだ。リンク先を見ている段階で混乱してきました。必要のないときは変数で渡さないようにしようと思いました。

2007/07/18 10:44:00
id:quintia No.5

回答回数562ベストアンサー獲得回数71

ポイント50pt
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 とでてきます。

  1. "hogeの中のa"という変数を確保 (実行されたhogeに束縛されたa とでも言うべき?)
  2. "hogeの中のa"に2を代入
  3. hugaの実行(1回目のhogeの1回目)→"1回目のhogeに束縛されたa"が3になり、"3"が表示される
  4. hugaの実行(1回目のhogeの2回目)→"1回目のhogeに束縛されたa"が4になり、"4"が表示される
  5. "hogeの中のa"という変数を確保
  6. "hogeの中のa"に2を代入
  7. hugaの実行(2回目のhogeの1回目)→"1回目のhogeに束縛されたa"が3になり、"3"が表示される
  8. hugaの実行(2回目のhogeの2回目)→"1回目のhogeに束縛されたa"が4になり、"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);

※の行が遅延して実行される様に時間設定しています。

こういう風に実行されるだと考えられます。

  1. グローバル変数bの確保と1の代入
  2. hogeの実行(1回目)
  3. "1回目のhogeに束縛されたa"の確保と、グローバル変数bの値1の代入
  4. b=b+10の実行によりグローバル変数bの値が11になる
  5. hugaの実行(1回目のhogeの1回目)→"1回目のhogeに束縛されたa"が2になり、"2"が表示される
  6. hogeの実行(2回目)
  7. "2回目のhogeに束縛されたa"の確保と、グローバル変数bの値11の代入
  8. b=b+10の実行によりグローバル変数bの値が21になる
  9. hugaの実行(2回目のhogeの1回目)→"2回目のhogeに束縛されたa"が12になり、"12"が表示される
  10. hugaの実行(1回目のhogeの2回目)→"1回目のhogeに束縛されたa"が3になり、"3"が表示される
  11. hugaの実行(2回目のhogeの2回目)→"2回目のhogeに束縛されたa"が13になり、"13"が表示される
  12. グローバル変数bの値、"21"が表示される

実行すると確かに 2,12,3,13,21 の順に表示されます。


解答、解説じゃなくてすみません。

質問が興味深かったので色々と実験してみた結果でした。

id:Lhankor_Mhy

えっと、難しいです。これは何かのイジメですか(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)

これでも実行結果同じなんですね。これは危険が危ない。

2007/07/18 10:07:06
id:todo36 No.6

回答回数34ベストアンサー獲得回数5

ポイント100pt

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...

この連載の次回に期待

id:Lhankor_Mhy

ああ、なるほど。

これは分かりやすいですね。有難うございます。

2007/07/18 10:26:50
id:suenaga3 No.7

回答回数20ベストアンサー獲得回数6

ポイント100pt

なるほど。

やっと疑問点が理解できました。


「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()の仕事は、引数のオブジェクトをタイマー(仮名)に渡すことだけなのです。

id:Lhankor_Mhy

なるほど。

大きく理解が進んだような気がします。


しかし、自分レベルのスクリプトだとバグを出してしまいそうな動作で、怪しい時にはsetTimeoutには変数を使わずリテラルで渡そうと決意しました。

2007/07/18 10:35:13
  • id:susie-t
    7の方の回答に関して、ちょっと一言。

    > そもそもローカル変数は、呼び出し元の関数と一緒に解放されて、
    > 実行時にはすでに参照できなくなっている事でしょう。

    これは違います。開放されず、残っています。これがクロージャのレキシカルスコープの特性です。

    各所で解説されていますので、参照してみてください。
  • id:susie-t
    私のほうでもまとめてみました。
    http://d.hatena.ne.jp/susie-t/20070719/1184821737

この質問への反応(ブックマークコメント)

トラックバック

  • prima materia diary - 関数もまたオブジェクト prima materia diary 2007-07-18 13:31:45
    var a=2; setTimeout(function(){alert(a)},2000); は、 function(){alert(2)} という関数オブジェクトが渡されます。 (aは変数なので、呼ばれた時点で値が評価される) javascriptに関する質問です。 a=1; function h
  • javascriptに関する質問です。 http://q.hatena.ne.jp/1184576878 そうか。fugaを普通の関数の形式で書いたのが良くなかったか。 a=10; function hoge(){ var a=1; var huga = function(){alert(a)};; setTimeout(huga
「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません