ECMA-262 でも W3C-DOM2 でも未定義であるクライアントサイドメソッド、 setTimeout について。
setTimeout メソッドは、引数のコードを一定時間後に実行する関数である。このメソッドは IE でも Netscape でも古くからサポートされているが、標準化はされていない。JavaScript と JScript、 2つの処理系におけるこの関数の実装の違いを見てみようと思う。
注: 正確には、 setTimeout メソッドは JScript としては定義されておらず、 JScript 処理系に Internet Explorer が提供するホストオブジェクト window のメソッドとして定義されている。
まず、 Netscape の JavaScript1.3 では、 setTimeout メソッドは次のように説明される。(以下要約) (http://developer.netscape.com/docs/manuals/js/client/jsref/window.htm#1203758)
指定ミリ秒後に、一度だけ式評価または関数呼出しをおこなう。
- 構文
- setTimeout(expression, msec)
- setTimeout(function, msec[, arg1[, ..., argN]])
- パラメータ
- expression -- JavaScript 式で構成される文字列。式は引用符で囲われなければならない; そうでなければ, setTimeout はそれを直ちに呼出す。例えば setTimeout("calcnum(3, 2)", 25)。
- msec -- ミリ秒単位の数値または文字列。
- function -- 任意の関数。
- arg1, ..., argN -- もしあれば、 function に渡す引数。
関数呼出しは JavaScript1.2 で追加された。
一方、 Misrosoft ではこの関数を次のように説明する。 (http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/settimeout.asp)
指定ミリ秒後に、一度だけ式評価または関数呼出しをおこなう。
- 構文
- iTimerID = window.setTimeout(vCode, iMilliSeconds [, sLanguage])
- パラメータ
- vCode -- 必須。 指定された間隔が過ぎた時に実行される関数のポインタまたはコードを示す文字列を指定する Variant。
- iMilliSeconds -- 必須。ミリ秒の数を指定する Integer。
- sLanguage -- 選択的。次の値のうち一つを指定する文字列: JScript: 言語は JScript である。 VBScript: 言語は VBScript である。 JavaScript: 言語は JavaScript である。
vCode に関数ポインタを指定できるのは IE5 以降である。
まず仕様上、 3 三番目以降の引数が異なる。 JavaScript では第一引数の関数の引数を指定し、 JScript では第一引数の文字列の言語を指定する。私見では引数指定できた方が使いやすいと思うんだが、でもまあ IE はさすが VBScript 他をサポートしているだけのことはあるといった感のある仕様だ。
順序でいくと、 JavaScript1.2(NN4.0-4.05) で追加された部分を IE5 が追っかけたことになるのかな。
setTimeout メソッドの引数に文字列で与えられるコードはグローバルスコープを持つと言われる。だが Netscape でも Microsoft もこの点に関する説明をしていない。少し考察してみよう。
実際、関数内部で setTimeout を呼出してもその関数内のローカル変数を参照することが出来ない。下のコードは 'HELLO_GLOBAL' というアラートを出す。
<script type="text/javascript"> function A (hello){ setTimeout( 'alert(hello);', 0 ); } A.hello = 'HELLO_FUNCTION_OBJECT' var hello = 'HELLO_GLOBAL'; </script> <a href="javascript:A('HELLO_A')">HELLO_A</a>
setTimeout メソッドの引数がグローバルコードとして評価されるなら、その中でグローバル変数を設定できてもよさそうなものである。下のコードは、まず hello_b をグローバル変数に設定し、 B() を呼出してその内部で hello_b の値を変更するコードを 0 秒後に評価する。グローバルコードとして評価されるなら、 hello_b の値は "HELLO_B_SETTIMEOUT" になっていなければならない。
<script type="text/javascript"> function B () { var hello_b = 'HELLO_B_INNER_FUNCTION'; setTimeout('var hello_b = "HELLO_B_SETTIMEOUT"',0); }; B.hello_b = 'HELLO_B_FUNCTION_OBJECT' var hello_b = 'HELLO_B_GLOBAL'; B() </script> <a href="javascript:alert(hello_b)">HELLO_B</a>
これはなかなか面白い結果になる。 alert(hello_b) で示される結果は、 IE では 'HELLO_B_GLOBAL'、Netscape では 'HELLO_B_SETTIMEOUT' になるのだ。 Netscape は完全にグローバルスコープと考えてよいような気がする。
ではこの IE のスコープはいったいなんなのだろう?
<script type="text/javascript"> function C () { var hello_c = 'HELLO_C_INNER_FUNCTION'; setTimeout('hello_c = "HELLO_C_SETTIMEOUT"',0); }; C.hello_c = 'HELLO_C_FUNCTION_OBJECT' var hello_c = 'HELLO_C_GLOBAL'; C() </script> <a href="javascript:alert(hello_c)">HELLO_C</a>
のように、 setTimeout 内のコードで var をつけなければ、 IE でも Netscape と同じようなグローバルスコープの hello_c を変更する。 var をつけないとグローバル変数…これは何かに似ていないか? そう、関数コードと同じだ。
もし、関数コードとして評価されているなら、変数 arguments が設定されるはずだ。試しに、次のようなコードを実行してみた。
<script type="text/javascript"> var hello_d; setTimeout('hello_d = arguments.callee',0); </script> <a href="javascript:alert(hello_d)">HELLO_D</a>
Netscape4/6 ではもちろん hello_d は undefined である。 IE ではというと、 hello_d の値 (つまり arguments.callee) として function anonymous () { hello_d = arguments.callee } という Function オブジェクトが設定されていた。このことから、 IE は setTimeout の引数をグローバルコードでなく関数コードとして実行していることが解る。
Issued: / Revised: / All rights reserved. © 2002-2017 TAKI