Scope of setTimeout Method

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 のスコープ

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>

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

HELLO_B

これはなかなか面白い結果になる。 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>

HELLO_C

のように、 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>

HELLO_D

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