JavaScript メモ by kanegon 2003.05.02 create 2003.05.28 update Microsoft JScript のメモ Netscape の仕様は未調査(予定なし) # 以下のスクリプトは正しく動作するつもりで書いているが、実際に実行してためし # たわけではない。そのまま入力しても動作しない可能性があります。 1. 型について MSDN の「JScript のデータ型」の説明では主要データ型として以下のものをあげて いるが、実際にはもう少しややこしい。 String (文字列) Number (数値) Boolean (ブール値) Object (オブジェクト) Array (配列) 特殊データ型を次に示します。 Null (ヌル) Undefined (未定義) すなわち、次のような 2つの定義があるとき、どちらも文字列型として一見同じよう に振舞う。 s1 = "hello1"; s2 = new String("hello2"); しかし、型情報を取り出すと異なっていることが分かる。 typeof(s1) => string typeof(s2) => object s1 instanceof String => 0 s2 instanceof String => -1 これは文字列に限らず、数値型など他の型でも同様である。 というわけで JavaScript で特定の型の判定を行う場合には typeof() や instanceof を直接使用するのではなく、以下のように別関数を作成して常に両方を チェックするのが安全。 function isString(obj) { if (obj == null) return false; return (typeof(obj) == "string" || obj instanceof String); } あと、上記結果から変数(オブジェクト)から型名を取得したいとき、typeof() で型 名が取得できる場合と取得できない("object" までは分かるがその実体が、Number な のか String なのかわからない)場合があることが分かる。 そのような場合でも、constructor プロパティから抽出できる(場合が多い)。 constructor プロパティは関数の定義を返すが関数名はオブジェクト名に等しいので 文字列化して関数名を抽出するとオブジェクト名になる。 これを関数化したものが以下。 # ちなみにうまく抽出できないものもある。 function typename(obj) { if (typeof(obj) == "object") { if ((""+obj.constructor).match(/^\s*function +(\w+)/)) { return RegExp.$1; } } return typeof(obj); } 2. クラス(もどき?)の作成 以下のような C++ のクラスを JavaScript で記述してみる。 [C++] class Counter { public: int m_nValue; Counter(int value) { m_nValue = value; } int SetValue(int value) { m_nValue = value; } GetValue(int value) { return m_nValue; } Inc() { return ++m_nValue; } Dec() { return --m_nValue; } } [JavaScript] // JavaScript では "関数の作成 == クラス定義" のような感じ // Counter という名前の関数を作成すると、そのままクラス定義を兼ねる // メンバ変数は this.XXX として初期化しておく function Counter(val) { this.value = val; } // メソッドを作成する // この時点ではまだクラスと関連付けられていない function Counter_SetValue(val) { this.value = val; } function Counter_GetValue(val) { return this.value; } function Counter_Inc() { return ++this.value; } function Counter_Dec() { return --this.value; } // valueOf() と toString() は必要に応じて標準の動作を上書きする function Counter_valueOf() { return this.value; } function Counter_toString() { return "" + this.value; } // 関数を定義した後でクラスとメソッドを関連付ける Counter.prototype.SetValue = Counter_SetValue; Counter.prototype.GetValue = Counter_GetValue; Counter.prototype.Inc = Counter_Inc; Counter.prototype.Dec = Counter_Dec; Counter.prototype.valueOf = Counter_valueOf; Counter.prototype.toString = Counter_toString; - 使い方 a = new Counter(10); a.Inc(); a.Inc(); a.Dnc(); WScript.Echo(a); // 結果として 11 が表示される // valueOf() または toString() が定義されていれば // GetValue() を使わなくても適切な文字列に変換される というわけでそれらしいものはできる。 しかし、デストラクタが仕様にないため、破棄のタイミングで特定の処理をさせるこ とはできず、明示的に後処理を行う必要がある。 VBScript のクラスにはそれらしいのがあるみたい(詳細未調査)なので、デストラク タが必要なら VBScript を使う(一部または全部)ことで実現可能(?) 3. 関数の動的生成 var add = new Function("x", "y", "return (x + y)"); とすると以下のように解釈される。 function add(x, y) { return (x + y); } 下の定義と違い、動的に生成する書式は eval() 内でも有効であり、ユーザの入力を 反映した任意の内容の関数を任意のタイミングで定義できるため、場合によってはか なり便利。 4. 他言語のスクリプト呼び出し 他言語の呼び出しは、その言語固有の便利な関数を利用するなどの局面で使うことが ある。 いくつかの方法があり、使用する局面によって異なる。 1) HTML 内のスクプリトとして 素直。 HTML 内の script タグで定義された関数は相互に呼び出し可能なので、特に考 えることなく、呼び出しが可能。 2) execScript() メソッドを使う (HTML 内のみ) 記述量は最小。 window オブジェクトに用意されている execScript() メソッドを使用する。 var n = 10; window.execScript('n = n * 2', 'vbscript'); 3) .wsh ファイルを介して両者を取り込み .wsh 内の script タグも HTML 内の script タグとほぼ同様に働く。 すなわち、特に考える必要はない。 4) ScriptControl を使用する ScriptControl を使用すれば任意の ActiveScript のコードを動的に生成し、実 行できる。 この方法はスクリプト内から別スクリプトを呼び出すことに限らず、例えば C++ 言語のアプリケーションから JavaScript のコードをマクロとして実行させるな どの使い方も考えられる。 var scVB = WScript.CreateObject("ScriptControl"); scVB.Language = "VBScript"; scVB.AddCode("Sub foo(s) : MsgBox s : End Sub"); scVB.Run("foo", "hello"); 5. 数値の扱いについて 整数は以下の範囲で表現される。 -999999999999999 〜 999999999999999 ただし、上記より大きい整数(即値含む)でも浮動小数点に 自動変換して処理され、エラーにはならない。 ビット演算では 32bit のみを用いて行われ、演算結果は上位 1bit を符号bitと見な して整数化された数値を返す。 そのため、ビット演算の結果は常に下記の範囲となる。 -80000000〜7fffffff この仕様により、下記のような直感に反する場合があるため注意すること。 0x80000000 | 0 => -2147483648 0x80000000 => 2147483648 また、以下の数値表現をサポートする。 NaN (非数): 数学的演算が不適切なデータ (文字列や未定義値) で実行された場合 に表示されます。 正の無限大: 正の数値が JScript で表記するには大きすぎる場合に表示されます。 負の無限大: 負の数値が JScript で表記するには小さすぎる場合に表示されます。 例えば 0 除算はエラーでなく、無限大の値となる。 WScript.Echo(1/0); => 1.#INF WScript.Echo(-1/0); => -1.#INF ※ この数値の仕様はドキュメントでなく、JScript 5.6 (on Windows2000) で 試した結果である。 他の環境で保証されるものかは不明。(ドキュメント探索中) 6. メモリ管理 JavaScript のメモリ管理はガベージ・コレクションであり、参照カウンタをベースと する VBScript とは挙動が異なる。 特に注意が必要なのは COM の扱いである。 インスタンス化した Excel などを終了させるとき、Quit() 後、参照の破棄を行うが、 実際のオブジェクトが破棄されるには null 代入後、ガベージ・コレクタが動作するま で待たなければならない。これにはかなり時間がかかることがあり、対策としては非公 開のメソッドである CollectGarbage() を呼ぶ必要がある。 ※ Quit() で画面上からは Excel が消えてもインスタンスは残っている。 http://support.microsoft.com/kb/266088/EN-US/ http://support.microsoft.com/kb/q164494/ なお、VBScript では Nothing の代入直後にオブジェクトは破棄される。