mixi engineer blog

*** 引っ越しました。最新の情報はこちら → https://medium.com/mixi-developers *** ミクシィ・グループで、実際に開発に携わっているエンジニア達が執筆している公式ブログです。様々なサービスの開発や運用を行っていく際に得た技術情報から採用情報まで、有益な情報を幅広く取り扱っています。

詳細 ECMA-262-3 シリーズ・第1章 実行コンテキスト/第2章 変数オブジェクト

初めましてこんにちは。たんぽぽグループの大形尚弘と申します。好きな言語は Dart です。どうぞよろしくお願いします。

さてもう昨年のことになりますが、私個人のブログにて、 Dmitry A. Soshnikov さんの JavaScript. The Core. という記事を翻訳させていただきましたところ、予想以上の反響をいただきました。 JavaScript の実装部分、例えば今なら HTML5 の色とりどりな API といったキラキラした部分だけでなく、 ECMAScript の仕様そのものに興味のある方が、こんなにいたなんて!

と、いうわけで、日本では、先日上梓されました『パーフェクト JavaScript 』でのみ触れられているような、 ECMAScript の言語仕様そのものについて、同じく Dmitry さんが書かれた ECMAScript3 および 5 に関する詳細記事シリーズを翻訳し、この場を借りてみなさまと共有させていただく許可をご本人からいただきましたので、是非ご一緒に、 ECMAScript への理解を深めていけたらと思います。興味のある方が今もいらっしゃったら嬉しいのですが。

まずは、第1章、第2章を翻訳させていただきました。以後、1週間に1度くらいのペースで追加していきますので、どうぞよろしくお願いします。

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

目次

  1. はじめに
  2. 定義
  3. 実行可能コードの種類
    1. グローバルコード
    2. 関数コード
    3. eval コード
  4. 結論
  5. 参考文献

はじめに

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

定義

コントロールが ECMAScript の実行可能コードに移るとき、コントロールは必ず実行コンテキストに入ります。
実行コンテキスト(以下 EC と略します)は、 ECMA-263 の仕様上で、実行可能コードを特徴毎に分類し、区別するために用いられる抽象的なコンセプトです。
仕様では、技術的な実装の観点から EC の種類や構造を正確に定義することはしていません。詳細はこの仕様を実装する ECMAScript エンジンに預けられています。 論理的には、アクティブな実行コンテキストの集合はスタックを形成します。スタックの最下は常にグローバルコンテキストであり、最上が現状の(アクティブな)実行コンテキストとなります。このスタックは、さまざまな種類の EC に出入りする際に変更( push/pop )されてゆきます。

実行可能コードの種類

抽象的なコンセプトである実行コンテキストは、それぞれ実行可能コードの種類が対応します。つまりコードの種類(グローバルコードなのか、関数コードなのか、 eval コードなのか)について話すとき、それは対応する実行コンテキストを意味しているとも言えます。 例えば、実行コンテキストのスタックを配列として定義してみましょう。
ECStack = [];
この場合、関数(例え再帰的には呼び出されたものであったり、コンストラクタとして呼び出されたものであっても)に入るとき、またはビルトインの eval 関数に入る際には、必ずスタックに新しい要素が push される、と考えることができます。

グローバルコード

この種類のコードは、 "Program" というレベルとして処理されます。すなわち、読み込まれた外部 .js ファイルや、ローカルのインラインコード( <script></script> タグの)です。(訳注: "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 による実装( Firefox や Thunderbird に導入)では、呼び出し元コンテキストを 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 第2章 変数オブジェクト

目次

  1. はじめに
  2. データ宣言
  3. 様々な実行コンテキストにおける変数オブジェクト
    1. グローバルコンテキストにおける変数オブジェクト
    2. 関数コンテキストにおける変数オブジェクト
  4. コンテキストコード処理のフェーズ
    1. 実行コンテキストへの進入
    2. コードの実行
  5. 変数について
  6. 実装系による機能: __parent__ プロパティ
  7. 結論
  8. 参考文献

はじめに

プログラムというものにおいて私たちは、関数及び変数を宣言することによりシステムを構築してゆきます。しかし、インタプリタは、どのように、そしてどこで、私たちのデータ(関数・変数)を見つけるのでしょうか?。私たちが必要なオブジェクトを参照するとき、何が起こっているのでしょうか? ECMAScript プログラマの多くは、変数が実行コンテキストと密接に関わっていることを知っています。
var a = 10; // グローバルコンテキストの変数

(function () {
  var b = 20; // 関数コンテキストのローカル変数
})();

alert(a); // 10
alert(b); // "b" is not defined(参照エラー)
同様に、現行バージョンの仕様においては、隔離されたスコープは "関数" 型コードからなる実行コンテキストによってのみ作られるということも、多くのプログラマの知るところです。すなわち C/C++ と比較したとき、例えば ECMAScript では for ループのブロックはローカルコンテキストを生成しません。
for (var k in {a: 1, b: 2}) {
  alert(k);
}

alert(k); // 変数 "k" は、ループが終了しても依然としてスコープ中に存在します。
私たちがデータを宣言するとき何が起こっているのか、より詳しく見てゆきましょう。

データ宣言

変数が実行コンテキストと関係があるならば、データがどこに保存され、データをどう取得するのか、実行コンテキストはわかっているはずです。このメカニズムを、変数オブジェクトと呼びます。
変数オブジェクト( Variable Object 以下 VO と略します)は、実行コンテキストに関連する特別なオブジェクトであり、そのコンテキスト中で宣言された以下の内容を保管します。

  • 変数(var 、 VariableDeclaration /変数定義)

  • 関数の定義( FunctionDeclaration 、以下 FD)

  • 関数の仮引数

定義上は、例として、変数オブジェクトを通常の ECMAScript オブジェクトとして表現することが可能です。
VO = {};
そしてこれまでご説明した内容から、 VO は実行コンテキストのプロパティですから、
activeExecutionContext = {
  VO: {
    // コンテキストのデータ(var 、 FD 、 関数の引数)
  }
};
間接的な変数の参照( VO のプロパティ名を通じた参照)は、グローバルコンテキスト(グローバルオブジェクトもまたそれ自体が変数オブジェクトなのですが、これは後述します)の変数オブジェクトにのみ許されています。その他のコンテキストにおいては、直接 VO を参照することはできません。これは純粋に実装上の仕組みです。

私たちが変数または関数を定義するということは、与えた名前と、変数の値をもった新しいプロパティを、 VO に生成することに他なりません。

例:
var a = 10;

function test(x) {
  var b = 20;
};

test(30);
このとき、対応する変数オブジェクトは次のように遷移します。
// グローバルコンテキストの変数オブジェクト
VO(globalContext) = {
  a: 10,
  test: <関数への参照>
};

// "test" 関数コンテキストの変数オブジェクト
VO(test functionContext) = {
  x: 30,
  b: 20
};
ただし、実装(および仕様)のレベルにおいては、変数オブジェクトは抽象的な要素です。具体的な、実際の実行コンテキストにおいては、 VO は異なる名称を与えられ、異なる初期構造を持つことでしょう。

様々な実行コンテキストにおける変数オブジェクト

いくつかの演算(例えば変数の具体化)や変数オブジェクトのふるまいは、全ての種類の実行コンテキストで共通しています。この観点から言えば、変数オブジェクトはベースとなる抽象的な存在であると捉えるとよいでしょう。そして、特に関数コンテキストにおいては、変数オブジェクトに追加の詳細が定義され得ます。
抽象的な変数オブジェクト(変数具体化プロセスにおける一般的なふるまい)

  ?
  ???>グローバルコンテキストの変数オブジェクト
  ?        (VO === this === global)
  ?
  ???> 関数コンテキストの変数オブジェクト
           (VO === AO, <arguments> オブジェクト、 <仮引数> が追加される)
これを詳しく見てみましょう。

グローバルコンテキストにおける変数オブジェクト

ここでまず先に、グローバルオブジェクトに定義を与えましょう。
グローバルオブジェクトは、どんな実行コンテキストに進入するよりも前に生成されるオブジェクトです。このオブジェクトはただ一つだけ存在し、そのプロパティはプログラムのどの箇所からも参照でき、グローバルオブジェクトのライフサイクルはプログラムとともに終了します。
グローバルオブジェクトは、例えば、 Math 、 String 、 Data 、 parseInt といった、グローバル関数やグローバルにアクセス可能な組み込みオブジェクトなどのプロパティとともに生成されます。その中には、グローバルオブジェクトそのものへの参照となるオブジェクトもあります。例えばそれは、 DOM 環境においては、グローバルオブジェクトの window プロパティがグローバルオブジェクトそのものを参照します(もちろん、全ての実装においてそうであるということではありません)。
global = {
  Math: <...>,
  String: <...>
  ...
  ...
  window: global
};
グローバルオブジェクトのプロパティを参照するとき、大抵プリフィックスは省略されます。なぜなら、グローバルオブジェクトにはその名前によって直接にアクセスすることはできないからです。ただし、グローバルコンテキスト中の this 値を通じて、または上述したように、グローバルオブジェクト自体への再帰参照(例えば DOM における window )を通じて参照可能です。従って、単純に以下のように書くことができます。
String(10); // global.String(10); を意味します

// プリフィックスを用いれば...
window.a = 10; // === global.window.a = 10 === global.a = 10;
this.b = 20; // global.b = 20;
つまり、グローバルコンテキスト上の変数オブジェクトに話を戻せば、変数オブジェクトとはグローバルオブジェクトそのものであるわけです。
VO(globalContext) === global;
このポイントはぜひ正確に理解してください。つまりこれによってこそ、私たちはグローバルコンテキスト中で宣言された変数を、グローバルオブジェクトのプロパティを通じて間接的に参照することができるからです(例えば、変数名が前もってわからないような場合であってもです)。
var a = new String('test');

alert(a); // 直接参照。 VO(globalContext) に見つかります。: "test"

alert(window['a']); // グローバルオブジェクト === VO(globalContext) 経由の間接参照: "test"
alert(a === this.a); // true

var aKey = 'a';
alert(window[aKey]); // 間接参照。動的なプロパティ名経由。: "test"

関数コンテキストにおける変数オブジェクト

関数の実行コンテキストに関しては、グローバルコンテキストとは異なり、 VO は直接アクセスされ得ず、アクティベーションオブジェクト( AO と略します)と特別に呼ばれる役割を果たします。
VO(functionContext) === AO;
アクティベーションオブジェクトは、関数のコンテキストに進入する際に生成され、 arguments プロパティとともに初期化されます。 arguments プロパティの値は、 Arguments オブジェクト です。
AO = {
  arguments: <ArgO>
};
Arguments オブジェクト はアクティベーションオブジェクトのプロパティです。このオブジェクトは次のプロパティを持ちます。
  • callee ― 現在の関数に対する参照
  • length実際に渡された実引数の数
  • プロパティインデックス( [n] )( 整数、または文字列に変換され得る)―値は、関数の実引数(左から右へ順に)の値です。これらのプロパティインデックスの数は arguments.length で表されます。 arguments オブジェクトのプロパティインデックスの値と、その時の、実際に内容が渡された仮引数の値は共有されます。(訳注:アクティベーションオブジェクトの直接のプロパティである関数の仮引数( x 、 y 、 z )と、アクティベーションオブジェクトのプロパティである arguments オブジェクトの数値インデックスによるプロパティ( arguments[0], arguments[1], arguments[2] )が、場合によって値を共有します。少し不思議な仕組みです)
例:
function foo(x, y, z) {

  // 定義された仮引数( x 、 y 、 z )の数
  alert(foo.length); // 3

  // 実際に渡された実引数( x 、 y のみ)の数
  alert(arguments.length); // 2

  // 関数自体への参照
  alert(arguments.callee === foo); // true

  // プロパティインデックス( arguments[n] )と、共有される引数値
  alert(x === arguments[0]); // true
  alert(x); // 10

  arguments[0] = 20;
  alert(x); // 20

  x = 30;
  alert(arguments[0]); // 30

  // しかし、仮引数 z には値が渡されていないため、
  // arguments オブジェクトの対応する数値のインデックスプロパティとは、
  // 値が共有されない。
  z = 40;
  alert(arguments[2]); // undefined

  arguments[2] = 50;
  alert(z); // 40

}

foo(10, 20);
最後のケースについては、現行バージョンの Google Chrome にはバグがあります。―(値の渡されていない)引数 z と、 arguments[2] の値が共有されてしまうのです。

コンテキストコード処理のフェーズ

さて、この章の中心ポイントまでやってきました。実行コンテキストコードの処理は、次の2段階に分けられます。
  1. 実行コンテキストへの進入
  2. コード実行
変数オブジェクトへの変更処理は、これら二つのフェーズに密接に関係しています。 これら2段階の処理のフェーズが、一般的なふるまいであり、コンテキストの種類(つまり、グローバル関数コンテキストです)とは独立していることには注意してください。

実行コンテキストへの進入

実行コンテキストに進入するとき(しかし、コード実行のにおいては)、 VO には次のプロパティが(次の順で)積み込まれます(冒頭で説明した通りです)。
  • 関数の各仮引数について(関数コンテキストであれば) ―仮引数の名前と実引数に与えられた値をもったプロパティを、変数オブジェクトに生成します。値が渡されなかった仮引数については、仮引数の名前と、 undefined を値にもったプロパティを VO に生成します。
  • 各関数の定義( FunctionDeclarationFD )について ―関数の名前と、関数オブジェクトを値に持ったプロパティを、変数オブジェクトに生成します。もし変数オブジェクトが同じ名前のプロパティを持っていた場合、その値と属性を置き換えます。
  • 各変数の定義( varVariableDeclaration)について ―変数の名前と、 undefined を値にもったプロパティを、変数オブジェクトに生成します。もしその変数名がすでに定義された関数の仮引数、または関数定義と同名であった場合、既存のプロパティを変更しません
例を見てみましょう。
function test(a, b) {
  var c = 10;
  function d() {}
  var e = function _e() {};
  (function x() {});
}

test(10); // 呼び出し
実引数 10 をもって "test" 関数に進入するとき、 AO は次のようになっています。
AO(test) = {
  a: 10,
  b: undefined,
  c: undefined,
  d: <関数定義 "d" への参照>
  e: undefined
};
AO が、 "x" プロパティを持っていないことに注意してください。これは、 "x" が関数定義ではなく、関数式( FunctionExpression 、 FE と略します)であり、 VO には影響を与えないことによるものです。しかし、関数 "_e" は同様に関数式であるにもかかわらず、後に見るように、変数 "e" に代入されることによって、 "e" という名前によって参照可能となります。関数定義関数式の違いについては、第5章 関数にて詳しく触れています。 以上の後に、コンテキストコード処理の第二段階に進みます。―コード実行フェーズです。

コードの実行

この時までに、 AO/VO にはすでにプロパティ(ただし、この段階ではその全てが我々によって渡される実際の値を持っているわけではありません。ほとんどは未だ初期値である undefined を値として持っています)が積み込まれています。 上記の例で考えると、コード実行時の AO/VO への変更は次のようになります。
AO['c'] = 10;
AO['e'] = <関数式 "_e" への参照>;
もう一度、関数式 "_e" が、宣言された変数 "e" に代入されたことによって、メモリ上に存在できていることを確認してください。そして、関数式 "x" は AO/VO に存在しません。すなわち、定義式の前、あるいは後に関数 "x" を呼び出そうとした場合、 "x is not defined" というエラーになるでしょう。保存されない関数式は、その定義式そのものによって、あるいは再帰的にのみ、呼び出すことができます。 もう一つ(古典的な)例を挙げてみましょう。
alert(x); // function

var x = 10;
alert(x); // 10

x = 20;

function x() {};

alert(x); // 20
なぜ、最初の "x" への alert 文が function となり、しかも定義より以前に参照可能なのでしょうか?。なぜ10や20ではないのでしょうか?。これはまさに前述した規則に従った結果です。― 関数定義は、コンテキスト進入時に VO に積み込まれます。全く同じフェーズ、コンテキスト進入時には同様に変数定義 "x" があります。しかし、前述したように、文法的に変数定義のステップは、関数及び仮引数定義の後に訪れ、その段階では、同名で定義された既存の関数及び仮引数定義を変更しません。従って、コンテキスト進入フェーズには、 VO には次の通りプロパティが生成されてゆきます。
VO = {};

VO['x'] = <関数定義 "x" への参照>

// var x = 10; という文を発見しました。
// もし関数 "x" が定義されていなければ
// "x" の値は undefined となったはずです。
// しかしここでは、変数定義は同名の関数の値を変更しません。

VO['x'] = <値は変更されていません。依然として関数への参照となります>
コード実行フェーズでは、 VO は次の通り変更されます。
VO['x'] = 10;
VO['x'] = 20;
二つ目、そして三つ目の alert の出力として見たとおりですね。 次の例では、再度、コンテキスト進入フェーズにて VO に変数が生成されることを見て取れます( else 節は決して実行されないのですが、 VO には変数 "b" が存在するのです)。
if (true) {
  var a = 1;
} else {
  var b = 2;
}

alert(a); // 1
alert(b); // undefined 。しかし、 "b is not defined" エラーとは違います。

変数について

JavaScript に関するいくつかの記事、あるいは書籍でさえも、時折「(グローバルコンテキスト)で var キーワードを使うか、(コード上のどんなところでも) var キーワードを使わなければ、グローバル変数を定義できる」と述べているのを目にします。これは誤りです。思い出してください。

変数は var キーワードを用いてのみ、定義されます。

そして、このような代入は...、
a = 10;
変数ではなく)新しいプロパティをグローバルオブジェクトに動的に生成しているのです。"変数ではない"とは、それが変更できないという意味ではありません。 ECMAScript における変数の概念において、"変数ではない"のです(そして、グローバルコンテキストの VO は グローバルオブジェクトそのものですから、グローバルオブジェクトのプロパティとなるわけです。覚えていますよね?)。 違いは次のようになります。例を見てみましょう。
alert(a); // undefined
alert(b); // "b" is not defined

b = 10;
var a = 20;
ここでまた、 VO とその変更のフェーズに関する問題となります(コンテキスト進入フェーズと、コード実行フェーズです)。 コンテキスト進入フェーズでは、
VO = {
  a: undefined
};
このように、 "b" が存在しません。なぜなら変数ではないからです。 "b" はコード実行フェーズに初めて出現します(しかし私たちの例ではエラーがあるのでそこまで到達しませんでしたね)。 コードを変えてみましょう。
alert(a); // undefined です。おわかりの通り。

b = 10;
alert(b); // 10 です。コード実行フェーズで(訳注:グローバルコンテキストの VO 、すなわちグローバルオブジェクトに対し、プロパティとして動的に)生成されました。

var a = 20;
alert(a); // 20 です。コード実行フェーズで変更されました。
変数にはもう一つ、重要なポイントがあります。単なるプロパティと異なり、変数は、 {DontDelete} 属性を持ちます。つまり、 delete 演算子によって変数を削除することができないということを意味します。
a = 10;
alert(window.a); // 10

alert(delete a); // true

alert(window.a); // undefined

var b = 20;
alert(window.b); // 20

alert(delete b); // false

alert(window.b); // 依然として 20
ただしこの規則が適用されない実行コンテキストがあります。それが、 eval コンテキストです。ここでは、変数に {DontDelete} 属性はセットされません。
eval('var a = 10;');
alert(window.a); // 10

alert(delete a); // true

alert(window.a); // undefined
これらのサンプルコードを、 Firebug のようなデバッグツールのコンソールでテストしている場合には注意が必要です。 Firebug は、コンソールから入力されたコードの実行に、まさに eval使っているからです。従って、 var された変数も {DontDelete} 属性を持たず、削除することが可能になっています。

実装系による機能: __parent__ プロパティ

すでに触れたように、仕様上では、アクティベーションオブジェクトに直接アクセスすることはできません。しかし、いくつかの実装、すなわち SpiderMonkey と Rhino においては、関数が特別なプロパティ __parent__ を持ちます。これは、関数が作られたアクティベーションオブジェクト(またはグローバル変数オブジェクト)への参照です。 例( SpiderMonkey 、 Rhino )
var global = this;
var a = 10;

function foo() {}

alert(foo.__parent__); // global

var VO = foo.__parent__;

alert(VO.a); // 10
alert(VO === global); // true
この例では関数 foo がグローバルコンテキスト上に作られ、従ってその __parent__ プロパティはグローバルコンテキストの変数オブジェクト、すなわちグローバルオブジェクトにセットされます。

ただし SpiderMonkey では、この方法ではアクティベーションオブジェクト取得できません。バージョンによりますが、内部関数の __parent__null またはグローバルオブジェクトを返すのです。

Rhino ではグローバルオブジェクトと同様の方法でアクティベーションオブジェクトにアクセスできます。

例( Rhino )
var global = this;
var x = 10;

(function foo() {

  var y = 20;

  // "foo" コンテキストのアクティベーションオブジェクト
  var AO = (function () {}).__parent__;

  print(AO.y); // 20

  // 現在のアクティベーションオブジェクトの __parent__ は、
  // グローバルオブジェクトです。
  // すなわち、変数オブジェクトの特別なチェーンが形成されているわけです。
  // これをスコープチェーンと呼びます。
  print(AO.__parent__ === global); // true

  print(AO.__parent__.x); // 10

})();

結論

この章では、実行コンテキストにまつわるオブジェクトについてさらに理解を深めました。この内容が、みなさんにとって曖昧だった部分を明確にし、役立ってくれることを期待します。以降の章では、スコープチェーン識別子解決、そしてその結果としてクロージャについて触れてゆきます。

何が疑問があれば、コメントにて喜んでお答えします(訳注:このブログではコメントをいただけないのですが、日本語で拙ブログにコメントいただければ、わかる限りで訳者も喜んでお答えします)。

参考文献

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

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