第7回 ecma-262 edition5.1読書会

47
ECMA 262 Edition 5.1 読書会 10実行可能コードと実行コンテキスト 10.4 10.6

Upload: shou-takenaka

Post on 18-Jun-2015

309 views

Category:

Technology


3 download

DESCRIPTION

ECMA-262 Edition 5.1 読書会 (http://ecma262reading.doorkeeper.jp/) 第7回の発表資料です。

TRANSCRIPT

Page 1: 第7回 ECMA-262 Edition5.1読書会

ECMA 262Edition 5.1 読書会

第10条 実行可能コードと実行コンテキスト(10.4 〜 10.6)

Page 2: 第7回 ECMA-262 Edition5.1読書会

About meShou Takenaka竹中 翔

@shoutakenaka

ソフトウェアエンジニア。最近はRoRとかAngularJSをやっています。

● https://twitter.com/shoutakenaka● https://github.com/shoutakenaka● https://plus.google.com/+ShouTakenaka/about● http://shoutakenaka.blogspot.jp/

Page 3: 第7回 ECMA-262 Edition5.1読書会

10.1〜10.3の復習

● ECMAScriptのコードが実行される時、実行コンテキストが

生成される。

※ 仕様書ではLexEnv => LexivalEnvironment、VarEnv => VariableEnvironmentとなっているが、LexicalEnvironment仕様型と区別が付きにくいので本スライドでは

略記。

Page 4: 第7回 ECMA-262 Edition5.1読書会

10.1〜10.3の復習

● 実行コンテキスト生成時は、LexEnv == VarEnv。● LexEnvは別のLexicalEnvironmentを参照することがある。

● VarEnvは不変。

● ResolveIdentifierは、LexEnv#GetIdentifierReferenceを呼

び出して識別子の解決を行う。

Page 5: 第7回 ECMA-262 Edition5.1読書会

10.1〜10.3の復習

● LexicalEnvironmentはECMAScriptコード中のスコープに

対応するデータ構造。

Page 6: 第7回 ECMA-262 Edition5.1読書会

10.1〜10.3の復習

● 値と識別子のバインディングを担当するEnvRecを持つ。

● 外側のLexicalEnvironment(=外側のスコープ)を表す

Outerを持つ。

● 識別子の解決(バインドされている値への参照の取得)を行

うGetIdentifierReferenceを持つ。○ 解決処理そのものはEnvRecに移譲。

● 新しいスコープに入る時、NewDeclarativeEnvironmentもし

くはNewObjectEnvironmentで子LexicalEnvironmentを作

成することができる。

Page 7: 第7回 ECMA-262 Edition5.1読書会

10.1〜10.3の復習

● EnvironmentRecordは識別子と値のバインディングを管理

する。

Page 8: 第7回 ECMA-262 Edition5.1読書会

10.1〜10.3の復習

● 識別子と値とのバインディングに関する各種操作を実装。

● DeclarativeEnvironmentRecord○ ローカル変数、関数のバインディングを管理するための

EnvironmentRecord。

○ DeclarativeEnvironmentRecord自体が内部でバインディング用のMapを持っているイメージ。

● ObjectEnvironmentRecord○ バインディングオブジェクトのプロパティのバインディグを管理するための

EnvironmentRecord。

○ 内部でバインディングオブジェクトの参照を持ち、このオブジェクトの内部メ

ソッドを呼び出してバインディングを管理する。

Page 9: 第7回 ECMA-262 Edition5.1読書会

10.1〜10.3の復習

Page 10: 第7回 ECMA-262 Edition5.1読書会

10.4 実行コンテキストの作成

● ECMAScriptコードが実行される時、実行コンテキストが生

成される。

○ globalコード

○ evalコード

○ functionコード

Page 11: 第7回 ECMA-262 Edition5.1読書会

globalコード実行時

● LexEnv、VarEnvにグローバル環境(10.2.3参照)、ThisBindingにglobalオブジェクトを設定した実行コンテキス

トが生成される。

Page 12: 第7回 ECMA-262 Edition5.1読書会

globalコード実行時

Page 13: 第7回 ECMA-262 Edition5.1読書会

globalコード実行時

var foo = 1;

function bar() { console.log(“bar”);}

console.log(window.foo); //=> 1window.bar(); //=> “bar”

Page 14: 第7回 ECMA-262 Edition5.1読書会

evalコード実行時

● 呼び出し元コンテキストが存在しない、もしくはdirect evalでない場合、globalコードの場合と同じ状態の実行コンテキス

トが生成される。

● そうでない場合は、呼び出し元実行コンテキストのLexEnv、VarEnv、ThisBindingと同じ値を参照した実行コンテキスト

が生成される。

Page 15: 第7回 ECMA-262 Edition5.1読書会

evalコード実行時

Page 16: 第7回 ECMA-262 Edition5.1読書会

globalコード実行時

eval( “var foo = 1;” + “function bar() {“ + “ console.log(‘bar’);” + “}”);

console.log(window.foo); //=> 1window.bar(); //=> “bar”

Page 17: 第7回 ECMA-262 Edition5.1読書会

evalコード実行時

● strictの場合は、LexicalEnvironmentのNewDeclarativeEnvironment()を呼び出し、LexEnvがOuterに設定されたLexicalEnvironmentを作成し、それを

LexEnv、VarEnvに設定する。

Page 18: 第7回 ECMA-262 Edition5.1読書会

evalコード実行時

Page 19: 第7回 ECMA-262 Edition5.1読書会

evalコード実行時

“use strict”;eval( “var foo = 1;” + “function bar() {“ + “ console.log(‘bar’);” + “}”);

console.log(window.foo); //=> undefined//window.bar(); //=> TypeError

Page 20: 第7回 ECMA-262 Edition5.1読書会

functionコード実行時

● 呼び出し時に指定されたthisArgをThisBindingに設定す

る。○ strictの場合はthisArgをそのままThisBindingに設定。

○ strictでない場合、

■ thisArgがnull、もしくはundefinedならglobalオブジェクトを

ThisBindingに設定。

■ null、undefined以外でObject型でない場合は、ToObjectして

ThisBindingに設定。

■ Object型なら、そのままThisBindingに設定。

● Functionオブジェクトの[[Scope]]内部プロパティの値

(8.6.2、13.2参照)を親にもつLexicalEnvironmentを生成

し、LexEnv、VarEnvに設定。

Page 21: 第7回 ECMA-262 Edition5.1読書会

functionコード実行時

Page 22: 第7回 ECMA-262 Edition5.1読書会

10.5 宣言的バインディングのインスタンス化

● 実行コンテキスト生成後、VarEnvが参照している

LexicalEnvironmentに、ローカル変数、関数、仮引数、

argumentsのバインディングが生成される。

a. functionコードの場合、各仮引数名を識別子、仮引数のインデックスに対応

する実引数を値(対応する実引数がない場合はundefined)として、バイン

ディングを設定。

b. コード中に関数定義が存在する場合は、関数名を識別子、Functionオブ

ジェクトを値として、バインディングを設定。

c. functionコード、かつargumentsがまだバインドされていない場合、10.6のアルゴリズムでargumentsを生成し、バインディングを設定。

d. コード中に変数宣言が存在する場合は、変数名を識別子、undefinedを値

として、バインディングを設定。

Page 23: 第7回 ECMA-262 Edition5.1読書会

10.5 宣言的バインディングのインスタンス化

● このようなアルゴリズムになっているので、いわゆるローカ

ル変数のホイスティング(全ての変数宣言が関数の先頭で

行われたように振舞う=関数の途中で変数を宣言している

場合に、変数宣言よりも上の行でその変数を参照してもエ

ラーにならない)が起こる。ただし、代入文を評価するまで値

はundefined。

function foo() { console.log(a); //=> undefined var a = 1; console.log(a); //=> 1}

Page 24: 第7回 ECMA-262 Edition5.1読書会

10.5 宣言的バインディングのインスタンス化

● 関数は、宣言的バインディングのインスタンス化時点で識別

子とFunctionオブジェクトがバインドされるので、いつでも呼

び出せる。

function foo() { bar(); //=> “bar called.” function bar() { console.log(“bar called”); } bar(); //=> “bar called.”}

Page 25: 第7回 ECMA-262 Edition5.1読書会

10.5 宣言的バインディングのインスタンス化

● argumentsよりも仮引数と関数のバインディングが先に行

われるので、仮引数か関数にargumentsという識別子のも

のがあると、argumentsオブジェクトが生成されない。

Page 26: 第7回 ECMA-262 Edition5.1読書会

(function(arguments) { console.log(arguments); //=> 1})(1, 2, 3);

(function() { console.log(arguments); //=> function arguments() {} function arguments() {}})(1, 2, 3);

(function() { console.log(arguments); //=> [1, 2, 3] var arguments = “arguments”; })(1, 2, 3);

Page 27: 第7回 ECMA-262 Edition5.1読書会

10.6 argumentsオブジェクト

● functionコード、かつ仮引数か関数定義でargumentsという

識別子が存在しない場合に、以下のアルゴリズムで

argumentsオブジェクトが生成される。a. 新しいECMAScriptオブジェクトobjを生成する。

b. objのプロトタイプに標準組み込みObjectのプロトタイプオブジェクトを設定

する。

c. 名前がlength、値が実引数の数のプロパティをobjに追加する。

d. 指定された各実引数に対し、名前が実引数の位置(ゼロベースのインデッ

クス)、値が実引数値となるプロパティをobjに追加する。

e. strictでない場合

■ 名前がcallee、値がfunctionコードに対応するFunctionオブジェクト、と

なるプロパティをobjに追加する。

f. strictの場合

■ TypeErrorをスローするFunctionオブジェクトthrowerを作る。

■ 名前がcaller、値がthrowerのプロパティをobjに追加する。

■ 名前がcallee、値がthrowerのプロパティをobjに追加する。

Page 28: 第7回 ECMA-262 Edition5.1読書会

10.6 argumentsオブジェクト

● strictでない場合、前述のcのステップの後に、arguments[n]と仮引数を同期させるための追加処理がある(p100 11のc)。○ strictでない場合は、arguments[n]を変更すると対応する仮引数の値も変

更される。逆も同様。

○ ちなみに、strictの場合はarguments[n]を変更しても対応する仮引数の値

は変更されないし、逆も同様。

Page 29: 第7回 ECMA-262 Edition5.1読書会

10.6 argumentsオブジェクト

(function(a, b) { console.log(arguments[0]); //=> 1 console.log(arguments[1]); //=> 2 console.log(a); //=> 1 console.log(b); //=> 2

arguments[0] = 3; b = 4;

console.log(arguments[0]); //=> 3 console.log(arguments[1]); //=> 4 console.log(a); //=> 3 console.log(b); //=> 4})(1, 2);

Page 30: 第7回 ECMA-262 Edition5.1読書会

10.6 argumentsオブジェクト

(function(a, b) { “use strict”;

console.log(arguments[0]); //=> 1 console.log(arguments[1]); //=> 2 console.log(a); //=> 1 console.log(b); //=> 2

arguments[0] = 3; b = 4;

console.log(arguments[0]); //=> 3 console.log(arguments[1]); //=> 2 console.log(a); //=> 1 console.log(b); //=> 4})(1, 2);

Page 31: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと

● 実行コンテキストがLexEnvとVarEnvという2つの

LexicalEnvironmentを保持しているのはなぜなのか?○ 10.3.1によると、識別子の解決はLexEnvだけを使っている。

○ じゃあVarEnvって意味あるの?

Page 32: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと

● VarEnvは何に使われている?○ 宣言的バインディングのインスタンス化(10.5)に使われる。

● つまり、実行コンテキスト生成後、実行コンテキストが参照

するVarEnvが保持するEnvironmentRecordに対して、バイ

ンディングの設定を行っている。

→ バインディングの設定時にはVarEnvを使って、解決時に

はLexEnvを使っているらしい。

○ p94に実行コンテキスト生成時、LexEnvとVarEnvは同じ値だと書いてあ

る。

○ じゃあ、LexEnvだけでよくない?分ける意味あるの?

Page 33: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと

● わざわざ分けているということは、LexEnvとVarEnvが別々

の値を指すことがあるはず。

○ with文(12.10 p152)とcatch節(12.14 p157)の評価時にLexEnvの値が変

わる。

● でも、VarEnvって実行コンテキスト生成直後にコード中の変

数や関数のバインディングに使った後は、特に何にも使わ

れていない様子。

○ 実行コンテキス生成時点でLexEnvとVarEnvが違う値を指していることがな

い限り、LexEnvとVarEnvの2つのLexicalEnvironmentを保持している意

味がないのでは?

Page 34: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと

● 10.4.2 evalコードの実行(p95-p96)によると、非strict環境

下でDirect evalすると、LexEnvとVarEnvはevalした時点の

実行コンテキストのLexEnvとVarEnvを引き継いだ実行コン

テキストが作成される。

→ ということは、LexEnvとVarEnvがあるのは、LexEnvとVarEnvが異なる状態でDirect evalした時のため?

Page 35: 第7回 ECMA-262 Edition5.1読書会

● 例えば下記のようなコードを考えてみる。

第6回読書会で疑問だったこと

(function() { with({}) { var a = 1; } console.log(a);})();

Page 36: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { // ここ with({}) { var a = 1; } console.log(a);})();

Page 37: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { with({}) { // ここ var a = 1; } console.log(a);})();

Page 38: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { with({}) { var a = 1; // ここ } console.log(a);})();

Page 39: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { with({}) { var a = 1; } // ここ console.log(a);})();

Page 40: 第7回 ECMA-262 Edition5.1読書会

● Direct evalで同じ処理をする場合、

第6回読書会で疑問だったこと

(function() { with({}) { eval(“var a = 1”); } console.log(a);})();

Page 41: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { // ここ with({}) { eval(“var a = 1”); } console.log(a);})();

Page 42: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { with({}) { // ここ eval(“var a = 1”); } console.log(a);})();

Page 43: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { with({}) { eval(“var a = 1”); // ここ(evalに入った直後) } console.log(a);})();

Page 44: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { with({}) { eval(“var a = 1”); // ここ(evalが終わった時点) } console.log(a);})();

Page 45: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { with({}) { eval(“var a = 1”); // ここ } console.log(a);})();

Page 46: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと(function() { with({}) { eval(“var a = 1”); } // ここ console.log(a);})();

Page 47: 第7回 ECMA-262 Edition5.1読書会

第6回読書会で疑問だったこと

● つまり、非strictの状態で、withやcatch内でDirect evalした

際に、evalしない場合と同じように外側の実行コンテキスト

のLexEnvに変数をバインドをする、という挙動をさせるため

にLexEnvとVarEnvを保持しているように思える。

● 下位互換性のためにこのような仕様になっている...?