Skip to content

Instantly share code, notes, and snippets.

@kajirikajiri
Last active May 24, 2020 05:26
Show Gist options
  • Save kajirikajiri/6d93e3dda22d883e6a2a0be422824165 to your computer and use it in GitHub Desktop.
Save kajirikajiri/6d93e3dda22d883e6a2a0be422824165 to your computer and use it in GitHub Desktop.
JavaScriptの実行コンテキストと実行スタック
* プログラム内に存在できるグローバル実行コンテキストは1つだけです。
たしかに1つだ。他にもあるかもと思ってた
* 評価関数の実行コンテキスト - eval関数内でのコードの実行には独自の実行コンテキストがありますが、JavaScript開発者が頻繁に使用evalすることはないため、ここでは説明しません。
こんなのあるのか
* 実行スタックは、他のプログラミング言語では「コールスタック」とも呼ばれ、コードの実行時に作成されるすべての実行コンテキストを格納するために使用されるLIFO(後入れ先出し)データ構造を持つスタックです。
LIFO(後入れ先出し)
* JavaScriptエンジンは、最初にスクリプトに遭遇すると、グローバル実行コンテキストを作成し、それを現在の実行スタックにプッシュします。エンジンは関数呼び出しに遭遇すると、関数の新しい実行コンテキストを作成し、それをスタックの一番上にプッシュします。
遭遇した順にプッシュ
* エンジンは、実行コンテキストがスタックの最上位にある関数を実行します。この関数の実行が終了すると、実行コンテキストがスタックからポップされ、制御フローは現在のスタックの次のコンテキストに到達します。
終わったらポップ
```javascript
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
```
https://user-gold-cdn.xitu.io/2018/9/20/165f539572076fe3?imageView2/0/w/1280/h/960/format/webp/ignore-error/1
* 上記のコードの実行コンテキストスタック。
* 上記のコードがブラウザーに読み込まれると、JavaScriptエンジンはグローバル実行コンテキストを作成し、それを現在の実行スタックにプッシュします。first()関数呼び出しに直面すると、JavaScriptエンジンは実行コンテキストの新しい関数を作成し、現在の実行のスタックの最上位に配置します。
ブラウザーに読み込まれるとグローバル実行コンテキストを作成。
* ときにfirst()内部の機能を呼び出すsecond()機能を、JavaScriptエンジンはするsecond()新しい実行コンテキストを作成して、現在実行中の関数のスタックの一番上にそれを置きます。ときにsecond()関数が完了し、その実行コンテキストスタックは、現在からポップアップ表示されます、および制御フローは、次の実行コンテキストになる、すなわち、first()実行コンテキスト機能。
関数呼び出しに達すると、関数をpush。内部に関数があればさらにpush。完了した順にpop.
* ときにfirst()終了、それがスタックからコンテキストポップを実行、制御の全体の流れは、実行コンテキストに達します。すべてのコードが実行されると、JavaScriptエンジンは現在のスタックからグローバル実行コンテキストを削除します。
最後にグローバル実行コンテキストを削除
* 実行コンテキストを作成するには?
* これまで、JavaScriptが実行コンテキストを管理する方法を見てきましたが、JavaScriptエンジンが実行コンテキストを作成する方法を理解しましょう。
* 実行コンテキストを作成するには、1)作成ステージと2)実行ステージの 2つのステージがあります。
気にならなかった。知れるなら知りたい。
```javascript
ExecutionContext = {
ThisBinding = <this value>,
LexicalEnvironment = { ... },
VariableEnvironment = { ... },
}
```
thisバインディング
LexicalEnvironmentコンポーネント
VariableEnvironmentコンポーネント
LexicalEnvironment
* 簡単に言えば、字句環境は、識別子と変数のマッピングを保持する構造です。(ここで、識別子は変数/関数の名前を指し、変数は実際のオブジェクト(関数オブジェクトと配列オブジェクトを含む)またはプリミティブ値への参照です)。
変数関数の名前、関数オブジェクト、配列オブジェクト、プリミティブ値への参照
例えば、つぎのスニペットを考えてみると
```javascript
var a = 20;
var b = 40;
function foo() {
console.log('bar');
}
```
lexicalEnvironmentはこのようになる
```javascript
lexicalEnvironment = {
a: 20,
b: 40,
foo: <ref. to foo function>
}
```
lexicalEnvironmentには2つのコンポーネントがある
Environment Record
変数と関数宣言が保存される実際の場所です。
Reference to the outer environment
その親のlexicalEnvironment(スコープ)にアクセスできることを意味します。
外部のlexicalEnvironmentにアクセスできることを意味します。つまり、JavaScriptエンジンは、現在のlexicalEnvironmentで変数が見つからない場合、外部環境内の変数を検索できます。
lexicalEnvironmentには2つのタイプがある
GlobalExectionContext
Type: "Object"のEnvironmentRecorder
FunctionExectionContext
Type: "Declarative"のEnvironmentRecorder
```
const person = {
name: 'peter',
birthYear: 1994,
calcAge: function() {
console.log(2018 - this.birthYear);
console.log(outer)
}
}
person.calcAge();
// 'calcAge'が// 'person'オブジェクト参照で呼び出されたため、 'this'は 'person'を参照します
// つまり、2018 - 1994になります
const calculateAge = person.calcAge;
calculateAge();
//オブジェクト参照が指定されていないため、「this」はグローバルウィンドウオブジェクトを参照します// まじか
```
```javascript
GlobalExectionContext = {
LexicalEnvironment:{
EnvironmentRecord:{
Type: "Object"、
// Identifier bindings go here
}
outer:<null>、 // そういうことかglobalのthisにはouterがない
this:<global object>
}
}
FunctionExectionContext = {
LexicalEnvironment:{
EnvironmentRecord:{
Type: "Declarative"、
// Identifier bindings here here
}
outer:<グローバルまたは外部関数環境参照>、 // これ呼べんの?
this:<関数の呼び出し方法に依存> // こいつがthisでよべんなら、outerで呼べんる? // よべなかったは // 無理そう https://stackoverflow.com/questions/2264037/access-outer-this-in-javascript-sort-method // 方法としては、外側で変数にthisを代入、もしくは、arrow関数を使う。それか.bind(this)でバインドする
}
}
```
Variable Environment
ES6では、lexicalEnvironmentComponentとEnvironmentVariableComponentの違いは前者は変数と関数宣言(let及びconst)バインディングの格納に使用される。後者はvar変数バインディングの格納に使用される
上記を理解するためのサンプル
```javascript
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
```
実行コンテキストはこうなる
```
GlobalExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
c: undefined,
}
outer: <null>
}
}
FunctionExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 在这里绑定标识符
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 在这里绑定标识符
g: undefined
},
outer: <GlobalLexicalEnvironment>
}
}
```
a,b(let,const)はどの値にも関連付けられていないが、c(var)はundefined。
これが、varに宣言より前にアクセスできる理由。巻き上げ。変数宣言の昇格
実行フェーズ
これは記事全体の中で最も単純な部分です。この段階で、これらすべての変数の割り当てが完了し、最後にコードが実行されます。
注 -実装フェーズでletは、変数の値を見つけるためにソースコードで宣言されたJavaScriptエンジンの実際の場所でない場合は、割り当てられundefinedます。
https://juejin.im/post/5ba32171f265da0ab719a6d7
https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment