読者です 読者をやめる 読者になる 読者になる

詳細 ECMA-262-3 第1章 実行コンテキスト

訳者によるまえがき

私が以前、「コア・JavaScript ( JavaScript. The Core. )」と題して、 ECMAScript に関する Dmitry Soshnikov さんのすばらしい記事を翻訳させていただいてから、1年が経ってしまいました。継続して Dmitry さんの記事を訳していくとお約束していながら、このように時間が経ってしまい、本当に申し訳ありません。健康上の理由から集中した時間を取ることができず、こうして技術文書を翻訳できたりするようになったのはごく最近のことです。

幸いにも、リハビリのような形で勤務先からこのシリーズの翻訳を業務として行えるよう許可をいただきましたので、こうして掲載させていただきます。また、 Dmitry さんの許可がいただければ、勤務先のブログにも掲載させていただく予定です。

この1年の間に、 node.js を初めとしたアプリケーション全般に関する ECMAScript の実装や、各ブラウザの ECMAScript 、特にバージョン5の実装が進み、また次期 ECMAScript となる ES.next/Harmony の話題も絶えず耳にはいるようになってきました。日本語でも、『パーフェクト JavaScript 』という唯一ともいえるすばらしい書籍が発売されました。そんな中、 ECMAScript3 にあたる、 ECMA-262-3 のシリーズの翻訳は多少時代錯誤に思われるかもしれませんが、 Dmitry さんが説明されるような内容のコアな部分に関しては、バージョンがあがっても変わっていくものではありません。必ずこれからの時代の ECMAScript 実装を取り扱うにあたり、役に立っていくものと思います。

今後は継続的に、遅くとも週に1章は翻訳を進めていきますので、ご一緒に ECMAScript への理解を深めてゆければと思います。

はじめに

この章では、 ECMAScript の実行コンテキストと、それに関連する実行可能コードの種類について説明します。


定義

コントロールが ECMAScript の実行可能コードに移るとき、コントロールは必ず実行コンテキストに入ります。

実行コンテキスト(以下 EC と略します)は、 ECMA-263 の仕様上で、実行可能コードを特徴毎に分類し、区別するために用いられる抽象的なコンセプトです。

仕様では、技術的な実装の観点から EC の種類や構造を正確に定義することはしていません。詳細はこの仕様を実装する ECMAScript エンジンに預けられています。

論理的には、アクティブな実行コンテキストの集合はスタックを形成します。スタックの最下は常にグローバルコンテキストであり、最上が現状の(アクティブな)実行コンテキストとなります。このスタックは、さまざまな種類の EC に出入りする際に変更( push/pop )されてゆきます。


実行可能コードの種類

抽象的なコンセプトである実行コンテキストは、それぞれ実行可能コードの種類が対応します。つまりコードの種類(グローバルコードなのか、関数コードなのか、 eval コードなのか)について話すとき、それは対応する実行コンテキストを意味しているとも言えます。

例えば、実行コンテキストのスタックを配列として定義してみましょう。

ECStack = [];

この場合、関数(例え再帰的には呼び出されたものであったり、コンストラクタとして呼び出されたものであっても)に入るとき、またはビルトインの eval 関数に入る際には、必ずスタックに新しい要素が push される、と考えることができます。


グローバルコード

この種類のコードは、 "Program" というレベルとして処理されます。すなわち、読み込まれた外部 .js ファイルや、ローカルのインラインコード( タグの)です。(訳注: "Program" を初めとした ECMAScript の構文上の構成要素に関しては、拙記事もご参照ください)。グローバルコードには、グローバルコード上の関数定義の本文に当たるコードは一切含まれません。

初期化時(プログラム開始時)には、 EC スタックは次のようになっているわけです。

ECStack = [
  グローバルコンテキスト
];
関数コード

関数コードに入るとき(全ての種類の関数において)、 EC スタックには新しい要素が push されます。その際、対象となる関数のコードとしては、内部関数のコードは決して含まれないことに注意してください。例えば、自己を再帰的に一度だけ呼び出す関数を例に取ってみましょう。

(function foo(bar) {
  if (bar) {
    return;
  }
  foo(true);
})();

この時、 EC スタックは以下の通り変更されてゆきます。

// 最初に foo という関数コンテキストをアクティベートします
ECStack = [
  <foo> 関数コンテキスト
  グローバルコンテキスト
];
 
// 再帰的にもう一度 foo 関数コンテキストをアクティベートします
ECStack = [
  <foo> 関数コンテキスト - 再帰的な
  <foo> 関数コンテキスト
  グローバルコンテキスト
];

return する度に現状の実行コンテキストが終了され、それに伴い EC スタックも pop されます。連続的に、上から下へと。ごく自然なスタックの実装です。 throw されたものの catch されなかった例外は、一つ、あるいは複数の実行コンテキストを終了します。このコードの作用が終了すると、 EC スタックには再度一つだけ、グローバルコンテキストが残ります。プログラムが完全に終了するまでグローバルコンテキストは残り続けます。


eval コード

eval コードに関してはより興味深い手続きがとられます。呼び出し元コンテキスト、つまり eval 関数が実行された、実行される元となったコンテキストが関わってくるのです。 eval によって実行されたアクション(例えば変数や関数定義など)は、呼び出し元のコンテキストに影響を与えることになります。

eval('var x = 10');
 
(function foo() {
  eval('var y = 20');
})();
 
alert(x); // 10
alert(y); // "y" は定義されていません

EC スタックの変更の様子です。

ECStack = [
  グローバルコンテキスト
];
 
// eval('var x = 10');
ECStack.push(
  eval コンテキスト,
  呼び出し元コンテキスト : グローバルコンテキスト
);
 
// eval がコンテキストを抜けます
ECStack.pop();
 
// foo 関数呼び出し
ECStack.push(<foo> 関数コンテキスト);
 
// eval('var y = 20');
ECStack.push(
  eval コンテキスト,
  呼び出し元コンテキスト: <foo> 関数コンテキスト
);
 
// eval から戻ります
ECStack.pop();
 
// foo から戻ります
ECStack.pop();

どうですか。とても一般的で、理にかなったコールスタックですよね。

バージョン 1.7 までの SpiderMonkey による実装( FirefoxThunderbird に導入)では、呼び出し元コンテキストを eval 関数の第2引数として渡すことが可能です。従って、そのコンテキストが存在する限り、 "プライベート" (とでも呼べるようなもの)変数に作用させることもできます。

function foo() {
  var x = 1;
  return function () { alert(x); };
};
 
var bar = foo();
 
bar(); // 1
 
eval('x = 2', bar); // コンテキストを渡し、内部変数 "x" に作用します
 
bar(); // 2

結論

この短い理論は、今後それぞれの章で取り扱われる、実行コンテキストにまつわる様々な詳細(例えば変数オブジェクトや、スコープチェーン等)を理解していくために必要となります。


参考文献

ECMA-262-3 仕様の対応する章 ― 10. Execution Contexts
(TAKIさんによる邦訳:10 実行コンテキスト (Execution Contexts)


英語版翻訳: Dmitry A. Soshnikov [英語版].
英語版公開日時: 2010-03-11

オリジナルロシア語版著者: Dmitry A. Soshnikov [ロシア語版]
オリジナルロシア語版公開日時: 2009-06-26


詳細 ECMA-262-3 シリーズ目次