Search
Duplicate

실행 컨텍스트와 자바스크립트의 동작 원리

1. Execution Context 란?

자바스크립트 코드가 실행되고 연산되는 범위를 나타내는 추상적인 개념.
우리가 코드를 작성하고 실행한다면 실행 컨텍스트(Execution Context) 내부에서 실행되고 있는 것입니다. 즉 코드들이 실행되기 위한 환경이자 하나의 박스이자 컨테이너라 볼 수 있습니다.

2. 자바스크립트 코드 실행에 필요한 정보

A. 변수: 전역 변수, 지역 변수, 매개 변수, 객체의 프로펕

B. 함수 선언

C. 변수의 유효범위

D. this

3. Types of Execution Context

A. Global Execution Context

기본 실행 컨텍스트로 함수 내부에 없는 코드는 전역 컨텍스트에서 실행된다고 생각하시면 됩니다. 이 전역 컨텍스트 안에서는 두 가지 작업을 수행합니다.
첫 번째, window 전역 컨텍스트를 생성합니다.
두 번째, this 를 전역 객체(Global Object)로 설정합니다.

B. Functional Execution Context

함수가 호출 될 때마다 해당 함수에 대한 새로운 실행 컨텍스트가 생성됩니다. 각 함수별로 실행 컨텍스트를 가지고 있지만 실행 컨텍스트는 함수가 호출될 때 만들어집니다. 함수 실행 컨텍스트(Functional Execution Context)는 얼마든지 있을 수 있으며 새로운 실행 컨텍스트가 생성될 때마다 차례대로 수행됩니다.

C. Eval Function Execution Context

eval 함수 내에서 실행되는 코드도 실행 컨텍스트를 가집니다. 하지만, 보안상의 문제로 eval은 잘 사용되지 않기 때문에 여기서는 따로 다루지 않습니다.

4. Execution Stack

다른 프로그래밍 언어에서는 호출 스택(call stack)이라 불리는것과 동일한 의미입니다. 스택은 LIFO(Last in, First out)구조 형태입니다.
Javascript 엔진은 script 요소를 처음으로 만나는 시점에서 Global Execution Context를 생성하고 Execution Stack에 push합니다.
그리고 엔진이 함수 호출을 찾을 때마다 해당 함수에 대한 새로운 실행 컨텍스트를 생성해 Execution Stack의 맨 위로 푸시합니다.
Javascript 엔진은 실행 컨텍스트가 스택에 맨 위에있는 함수를 실행한 뒤 함수가 종료되면 스택에서 제거한 뒤 호출 스택은 최신화된 스택에서 맨위의 컨텍스트를 이전과 동일한 로직으로 접근합니다.
let a = "Notion posting"; 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'); /* Inside first function Inside second function Again inside first function Inside Global Execution Context */
JavaScript
복사
예제 코드의 실행 컨텍스트 스택 흐름

5. Execution Context 생성 로직

지금까지는 자바스크립트 엔진에서 어떻게 실행 컨텍스트를 관리하고 동작하는지에 대해 알아보았다면, 이번에는 엔진이 실행 컨텍스트를 만드는 과정에 대해 알아봅니다. 실 행 컨텍스트는 1) Creation Phase , 2)Execution Phase의 두 단계로 생성됩니다.

1. Creation Phase

실행 컨텍스트는 생성 단계에서 생성되는데 이 단계에서는 두 가지 일이 일어납니다.
Lexical Environment 구성 요소가 생성됩니다.
Variable Environment 구성 요소가 생성됩니다.
실행 컨텍스트는 개념적으로 아래와 같이 나타낼 수 있습니다.
ExecutionContext ={ LexicalEnvironment = <ref. to LexicalEnvironment in memory>, VariableEnvironment = <ref. to VariableEnvironment in memory>, }
JavaScript
복사

1.1 Lexical Environment

identifier-variable mapping되는 곳. 다시 말하자면 Identifier Resolution 식별자 해결을 하기위한 공간입니다.
참조 대상 식별자인 identifier는 함수와 변수의 이름과 같이 식별자로 Unique합니다. 자바스크립트는 이 규칙대로 식별자를 찾습니다.
즉, 변수와 해당 변수에 대입된 값이 매핑되는 곳이 Lexical Environment라 할 수 있습니다.
아래 코드를 통해 좀 더 자세히 알아봅니다.
//code snipet var a = 20; var b = 40; function foo(){ console.log('bar') }
JavaScript
복사
Lexical Environment에서는 세 가지 구성 요소가 있습니다.
1.
Enviroment Records
2.
Reference to the outer environment
3.
This binding
//Lexical Environment lexicalEnvironment = { a : 20, b : 40, foo : <ref. to foo function> }
JavaScript
복사

1. Environment Records

Lexical Environment안에 함수와 변수선언을 저장하는 곳입니다.
Declarative environment record — 변수와 함수 선언을 저장합니다.
Object environment record — 전역코드의 Lexical Environment는 객체 환경 레코드(Object environment record)를 포함합니다. 변수와 함수의 선언과는 다르게 전역 객체(window) 도 저장합니다. 따라서 각각의 객체의 속성을 바인딩하기 위해 record에는 새로운 엔트리가 생성됩니다.

2. Reference to the outer environment

외부 환경에 대한 참조는 외부에 있는 Lexical Environment환경으로 접근할 수 있다는 의미합니다. 즉, Javascript 엔진은 현재의 Lexical Environment에서 변수를 찾지 못했다면 외부 환경에서 해당 변수를 찾아 볼 수 있다는 의미입니다.

3. This binding

this 의 값 할당이 여기서 결정됩니다. 전역 실행 컨텍스트(Global Execution Context)에서 this는 전역객체입니다. (window) 함수 실행 컨텍스트에서의 값은 this 함수가 호출되는 방식에 따라 다른데 함수가 객체 참조에 의해서 호출되면 해당객체로 설정되고 그렇지 않을 경우 전역 객체(window)를 가르키거나 strict mode에서는 undefined 를 가르킵니다.
const person ={ name: "hansol", birthYear: 1988, calcAge: function(){ console.log(2020 - this.birthYear); } } person.calcAge();//32 ---(1) const calculateAge = person.calcAge; calculateAge();//NaN ---(2)
JavaScript
복사
⇒(1): calcAge는 person을 참조객체로 호출했기 때문에 여기서 this는 person을 가르키게되어 this.birthYear는 1988이되어 32가 나옵니다.
⇒(2): calculateAge()는 객체참조값이 없다 앞에 EntityGraph의 탐색을 시작하는 . 도없기에 전역 객체를 참조하는 것으로 가정하기에 this는 window 전역 객체를 가르키게되고 전역객체에는 birthYear라는 프로퍼티가 없기 때문에 NaN이 나옵니다.

1.2 VariableEnvironment

실행 컨텍스트 안에서 식별자로부터 Environment Records에 바인딩된 것들이 Lexical Environment입니다.
위에 작성했던 내용대로 VariableEnviroment 또한 LexicalEnvironment이고, 위에서 정의했던대로 LexicalEnviroment의 모든 프로퍼티와 요소를 가집니다.
ES6에서 한가지 차이점이 생기는데, VariableEnvironment는 변수 var만 저장하고 LexicalEnvironment는 함수 선언과 변수(letconst) 바인딩을 저장합니다.

2. Execution Phase

이 페이즈에서는 모든 변수에 대한 값 할당이 완료되고 최종적으로 코드들이 실행됩니다.
아래 예제를 통해 실행 컨택스트의 2단계(Creation, Execution)에 대해 알아봅니다.

Example

let a = 20; const b = 30; var c; function multiply(e, f){ var g = 20; return e * f * g; } c = multiply(20, 30);
JavaScript
복사
1.
위 코드가 실행될 때 자바스크립트 엔진은 Global Execution Context를 만들거 전역 변수중 함수let, const 는 Lexical Environment로 var 는 VariableEnvironment로 key/value로 매핑됩니다.
GlobalExecutionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Object", //Identifier bindings go here a: < uninitialized >, b: < uninitialized >, multiply: < func > } outer: <null> ThisBinding: <Global Object> }, VariableEnvironment: { EnvironmentRecord: { Type: "Object", // Identifier bindings go here c: undefined, } outer: <null> ThisBinding: <Global object> } }
JavaScript
복사
2. execution phase가 실행되는 동안 각각의 변수 할당이 수행됩니다.
GlobalExecutionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Object", //Identifier bindings go here a: 20, b: 30, multiply: < func > } outer: <null> ThisBinding: <Global Object> }, VariableEnvironment: { EnvironmentRecord: { Type: "Object", // Identifier bindings go here c: undefined, } outer: <null> ThisBinding: <Global object> } }
JavaScript
복사
3. multiply(20, 30) 함수가 호출되는 시점에서 새로운 함수 실행 컨텍스트(Function Execution Context)는 함수코드를 실행합니다.
그리고, 함수 실행 컨텍스트는 생성 단계(Creation phase)와 유사한 형태일 것입니다.
FUnctionExecutionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", //Identifier bindings go here Arguments: {0: 20, 1: 30, length: 2}, }, outer: <GlobalLexicalEnvironment>, ThisBinding: <Global Object or undefined>, }, VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", //Identifier bindings go here g: undefined }, outer: <GlobalLexicalEnvironment>, ThisBinding: <Global Object or undefined> } }
JavaScript
복사
4. 그 다음, 실행 컨텍스트는 함수 내부의 변수들에 할당하는 실행단계를 거칩니다. 따라서 함수 실행 컨텍스트(Functional Execution Context)는 아래와 같은 모습을 보입니다.
FUnctionExecutionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", //Identifier bindings go here Arguments: {0: 20, 1: 30, length: 2}, }, outer: <GlobalLexicalEnvironment>, ThisBinding: <Global Object or undefined>, }, VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", //Identifier bindings go here g: 20 }, outer: <GlobalLexicalEnvironment>, ThisBinding: <Global Object or undefined> } }
JavaScript
복사
위와같이 함수가 완료된 이후 반환된 값을 내부에 저장됩니다. 그리고 Global Lexical Environment는업데이트 됩니다. 그 후 전역 코드가 완료되고 프로그램은 완료됩니다.

참조