JavaScriptとは何なのか?基本的な特徴
JavaScriptは非常にシンプルな言語であり、インタプリンタ言語として設計されている。プログラミング言語JavaはJavaScriptとは別物である。特徴としては次の様になものがあげられる。
- ポインタ演算を除き、何となくCから影響を受けている構文
- シンプルなクラスレスのオブジェクトモデル(Self言語の影響)
- 自動的なガベージコレクション
- 動的で弱い型付け
通常のウェブブラウザの実装におけるXMLHttpRequestやインラインフレームを使った通信では、セキュリティ上の制限から他のドメインのリソースにアクセスすることができない。
JavaScript自体には組み込みのI/O機構はない。ブラウザ内では、あらかじめ定義されている一連のメソッドやプロパティを介して、そのホスト環境とやり取りする程度の事は可能であるが、他の多くのプログラミング言語とは異なり、これらのインターフェイスは限定的で特化した機能しか持ち合わせていなのである。簡単なJavaScriptのプログラムは以下参照。
[javascript]
var text = "何処かでお会いしましたか?";
function display_string(str) {
alert(str);
return 0;
}
//"何処かでお会いしましたか?" を表示
display_str(text);
[/javascript]
さて、基本的な事に関しては他のサイトに譲るとして以降はセキュリティ関連の特性を説明しよう。
スプリクト処理モデル
ブラウザに表示される全てのHTMLドキュメントには、独りしたJavaScript実行環境のインスタンスが付与される。この実行環境には、読み込んだスクリプトで作成されるグローバル変数や関数が保存される単一の名前空間が備わっている。特定のドキュメントのコンテキストで実行される全てのスクリプトが、この共通サンドボックスを共有している事になり、またスクリプトはブラウザが提供するAPIを使用して他のコンテキストとのインタラクションを行なう事も可能なのである。このようなドキュメント間でのインタラクションは明示的に行なう必要があり、インタラクションを明示的に行なう事によりドキュメント間で無意識に相互に干渉してしまう可能性を低くする事が出来るのである。
特定の実行コンテキスト内ではそのコンテキストに含まれる全てのJavaScriptブロックは個別に実行される事となります。各ブロックは以下の3つでである。
- パース
- 関数の解決
- コードの実行
各コードブロックは任意の自己完結型で適切な構文を持った構文単位をその構文要素とする必要がある。以下個別に見て行く事にしよう。
1.パース
パースステージでは、スクリプトブロックの構文が検証されると共に、通常はスクリプトブロックから中間的なバイナリ表現への返還も行なわれる。コードがグローバルに影響を及ぼす事は皆無だが構文エラーが発生した場合には、問題を起こしたブロック全体がバッサリと棄て去られ、パーサーはコード内で次に処理可能なチャンクへと処理を進める事となる。
では、JavaScriptがパーサーがどんなもんか見て行くことにしよう。
[html]
<script> //ブロック1
var my_variable1 = 1;
var my_variable2 =
</script>
<script>
2; //ブロック2
</script>
[/html]
C言語をマイン言語として習得した人にとっては、上のコードシーケンスと下のコードシーケンスが等価だと感じるだろう。
[html]
<script>
var my_variable1 = 1;
var my_variable2 = 2;
</script>
[/html]
しかし、全く異なるものであり別次元のものなのである。等価とならないのは、パースの前にあらかじめ<script>ブロックが連結されていないからである。その為最初のスクリプトブロックは単に構文エラーを発生し、結果ブロック全体はその存在を一切無視される事となり実行ステージへとたどり着けなくなってしまうのである。グローバルな副作用が発生する前にブロック全体が切り捨てられると言う事は以下のコードとも等価でないと言う事である。
[html]
<script>
var my_variable1 = 1;
</script>
<script>
2;
</script>
[/html]
これがBashなど他の多くのスクリプト言語との大きな大きな違いでるのだ。では、最初に記述したサンプルコードのパースで何が起こるのかというと、最初のブロックは無視されるのだが、2つ目のブロック(<script>2;</script>)は適切にパースされるのだ。ただし、実行時にno-opとみなされ、コードステージメントとして数値だけが記述される形となることは申し添えておく。
2.関数の解決
パースステージが完了すると次のステップではパーサーが現在処理を行なっているブロック内で発見した、名前を持つグローバル関数全ての登録を行なうこととなり、登録が行なわれると、パーサーが発見した各関数はその後のコード実行で利用できるようになるのである。実行前にこのステップが入る事により、以下の構文が正しく実行出来るようになる。
[html]
<script>
hello_japanese_p();
function hello_japanese_p() {
alert(‘何処かでお会いしましたか?’);
}
</script>
[/html]
逆にこれをいじってしまった以下のコードは思った通りに作動できない。
[html]
<script>
hello_japanese_p();
</script>
function hello_japanese_p() {
alert(‘何処かでお会いしましたか?’);
}
</script>
[/html]
実行エラーとなるのだが、何故かと言うと、個々のコードブロックは同時に処理されずに、JavaScriptエンジンでの処理が可能になった順列に従って処理するからに他ならない。一つ目のブロックを実行する準備が整った段階においては、hello_japanese_p()を定義しているブロックのパースはまだ行なわれていないのである。
ここで見たグローバル名解決は関数だけに適用され、変数宣言には適用されない為に、難解になってしまう要因であろう。変数は、実行時に連続して登録され、この点に関しては他のインタプリンタ型のスクリプト言語と似ているといえるであろう。この為、以下のサンプルコードは思ったように動いてはくれない。(グローバルなhello_japanese_p()を、グローバル変数に代入されている無名関数に置き換えてみた)
[html]
<script>
hello_japanese_p();
var hello_japanese_p = function() {
alert(‘何処かでお会いしましたか?’);
}
</script>
[/html]
この場合、hello_japanese_p()を呼びだそうとしているタイミングでは、まだhello_japanese_p()変数への代入が行なわれていない。
3.コードの実行
関数の解決が終了したら、JavaScriptエンジンは通常、関数ブロックに含まれていないステートメント全てを順次実行していくことになるが、この段階では、未処理の例外によって、あるいはよくわからない原因でスクリプトの実行が失敗する事がある。ただし、エラーが発生してもエラーを起こしたコードブロック内にある解決済みの関数は依然として呼び出し可能な状態におかれており、また、それまでに実行されたコードによりもさらされた状態変化も現在のスクリプト実行コンテキストの中でその状態を保つこととなる。
以下に示すサンプルコードは非常に興味深い点があり、JavaScriptにおける例外処理と実行特性が見える濃縮度の高いサンプルコードと言えるだろう。
[html]
<script>
↓この関数は何処からも呼び出されないので実行されない。
function not_called() {
return 72;
}
↓この関数は呼び出された時のみ実行される。ダイアルログ
の表示は行なうが、do_stuff()という名前の関数への参照が
解決されていないので例外を吐き出す。
function hello_japanese_p() {
alert(‘何処かでお会いしましたか?’);
do_stuff();
}
↓このステートメントからプログラムが実行される。
alert(‘はじめまして。名刺交換宜しいですか?’);
↓それから「何処かでお会い~」メッセージが表示される。
hello_japanese_p();
↓未処理の例外がhello_japanese_p()で吐き出された為
ここまでは届かない。
alert("一枚でよかったでしょうか?");
</script>
↓これは独立したブロックなので上で例外が発生しても、この
実行が妨げられる事無く実行される。
<script>
alert("私になんでも聞いて下さいね。");
</script>
[/html]
この例からも分かる通り、予期せぬ例外が発生した際にきちんと処理を行なわないと、どの様な結果が待ち受けているか想像もできない。例外によって、アプリケーションの状態が不安定になるかもしれないし、その状態でも何とかこうとか実行出来るかもしれない。例外とは、予想外のエラーが発生した際にそれが伝達していく事を避ける為の手段であり、それゆえにgotoステージメントの禁止などの関連から、JavaScriptはより原理主義的な姿勢を貫き通すのであった。
それでは、次回のJavaScriptシリーズでは実行順序の制御より更に理解を深めていきましょう。