[JavaScript]실행 컨텍스트(Execution Context)

LMH·2022년 10월 30일
1
post-thumbnail

이번에는 자바스크립트를 엔진이 동작원리를 담고 있는 자바스크립트의 핵심원리이며, 코드 해석과 디버깅 능력 향상을 위해 실행 컨텍스트에 대해 정리하고자 합니다.

실행 컨텍스트(Execution Context)란?

ECMAScript 스펙에 따르면 실행 컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이라 정의합니다. 쉽게 표현하면, 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아 놓은 객체입니다.

소스 코드가 로드되면 자바스크립트 엔진은 전역 코드를 평가하게 되는데 평가 절차는 다음과 같습니다.

    1. 전역 실행 컨텍스트(Global Execution Contex) 생성
    1. 전역 환경 레코드(Global Environment Record) 생성
    1. 객체 환경 레코드(Object Environment Record) ,선언적 환경 레코드(Decla Environment Record) 생성
    1. this 바인딩
    1. 외부 렉시컬 환경에 대한 참조 결정

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

자바스크립트 엔진은 다음과 같은 코드 실행에 필요한 여러가지 정보를 미리 파악합니다.

  • 변수 : 전역변수, 지역변수, 매개변수, 객체의 프로퍼티
  • 함수 선언
  • 변수의 유효범위(Scope)
  • this

실행 컨텍스트의 타입

1. 전역 실행 컨텍스트(Global Execution Context)

기본 실행 컨텍스트로 함수 내부에 없는 코드는 전역 컨텍스트에서 실행하게 되며, window 객체를 만들고 전역 컨텍스트의 this를 window 객체로 할당합니다.

2. 함수 실행 컨텍스트(Functional Execution Context)

함수가 호출 될 때마다 해당 함수에 대한 새로운 실행 컨텍스트가 생성되고 각 함수별로 실행 컨텍스트를 가지고 있지만 실행 컨텍스트는 함수가 호출될 때 만들집니다.

3. EVAL 실행 컨텍스트(Eval Function Execution Context)

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

실행 컨텍스트의 Call Stack

let a = 0;
function foo() {
  let b = 5;

  function foo2() {
  let c = 3;
	}
 foo2();
}
foo();

위의 코드를 보면 foo() 함수 내부에 foo2()함수가 선언되어 있습니다. 이 경우 실행 컨택스트의 생성과 소멸 단계는 다음과 같습니다.

1. 자바스크립트 엔진이 작동하면서 전역 실행 컨텍스트가 생성되어 callstack에 추가됩니다.

  • callstack에서는 가장 위에 있는 실행 컨텍스트가 활성화되며, 자바스크립트 엔진은 활성화된 실행 컨텍스트 부터 읽어 나가기 시작합니다.

2. foo() 호출하고 foo() 실행 컨텍스트가 callstack에 추가됩니다.

  • foo() 실행 컨텍스트가 활성화되고, foo() 내부의 코드를 읽어 나갑니다.

3. foo2() 호출하고 foo2() 실행 컨텍스트가 callstack에 추가됩니다.

  • foo2() 실행 컨텍스트가 활성화되고, foo2() 내부의 코드를 읽어 나갑니다.

4. foo2()가 종료되고 foo2() 실행 컨텍스트가 callstack에서 제거됩니다.

5. foo()가 종료되고 foo() 실행 컨텍스트가 callstack에서 제거됩니다.

6. 전역 실행 컨텍스트가 종료되고 callstack에서 제거됩니다.

실행 컨텍스트의 생성 로직

1. 생성 단계(Creation Phase)

컨텍스트 생성 단계에서는 두 가지 일이 발생합니다.

  • Lexical Environment 구성 요소가 생성
  • Variable Environment 구성 요소가 생성
    생성 단계에서는 선언문만 실행해서 Environment Record에 기록합니다.
 ///실행 컨텍스트의 개념
  ExecutionContext ={
	LexicalEnvironment = <ref. to LexicalEnvironment in memory>,
	VariableEnvironment = <ref. to VariableEnvironment in memory>,
}

1-1 렉시컬 환경(Lexical Environment)

스코프를 구분하여 식별자를 기록하고 관리한는 저장소 역할을 하며 환경 레코드(Environment Record), 외부환경에 대한 참조(Reference to the outer environment) 두 가지 컴포넌트로 이루어져 있습니다.

렉시컬 환경은 환경 레코드(Environment Record)와 외부환경에 대한 참조(Reference to the outer Environment)로 이루어져 있습니다.

  Environment Record

환경 레코드(Environment Record)는 렉시컬 환경안의 함수와 변수 등을 기록합니다.

  • 전역 환경 레코드(Global Environment record)
    전역 환경 레코드(Global Environment record)는 *realm에서 처리되는 가장 바깥의 스코프를 나타내는 데 사용됩니다.. 전역 환경 레코드는 빌트인 전역 객체, 전역 객체의 프로퍼티 및 스크립트 내의 모든 최상위 선언에 대한 바인딩을 제공합니다.

    - Realm : 어떤 코드든 평가되기 전, realm에 연결되어야 합니다. realm은 객체, 전역 환경, 전역 스코프에서 로드되는 모든 코드, state 등으로 구성됩니다.

    이론상 하나의 레코드이지만, 실제로는 '객체 환경 레코드(Objcect Environment record)'와 '선언 환경 레코드(Declarative Environment Record)'로 구성됩니다.

    전역 환경의 객체 환경 레코드는 Realm Record의 전역 객체와의 바인딩을 관리합니다. 또한 빌트인 전역 객체(windows나 global 등)의 바인딩, 전역에서 선언된 모든 코드에 대한 바인딩을 제공합니다.
  • 선언적 환경 레코드(Declarative Environment Record)
    - var, const, let, class, module, import, function 등으로 스코프 내에서 선언된 식별자들의 바인딩을 관리합니다.
consol.log(a) // undifined
consol.log(b) // Referencd error
  
var a = '학생';
let b = '학생';
function study(c, d) {
  reutrn c + d;
}
  
study(10, 10)
  
// 환경 레코드에 다음과 같이 선언문이 있는지 스캔하여 식별자를 기록
// { a = 'undifined' } { b =  }  { study : f {}}

// var 키워드 변수의 경우 'undefined'로 초기화
// let, const 키워드 변수의 경우 초기화 되지 않음
// 함수의 경우 선언과 동시에 초기화

[참고]
let, const로 변수를 선언할 경우, 실행 컨텍스트 생성 단에서 'undefined'로 초기화 되지 않아 값이 할당되기 전까지 변수 참조가 불가능합니다. 이를 '일시적 사각지대(Temporal Dead Zone)'라고 부릅니다.

  • 객체 환경 레코드(Object Environment record)
    객체 환경 레코드는 Binding Object라는 객체와 연결됩니다. Binding Object의 프로퍼티 이름에 문자열 타입의 식별자 이름들을 바인딩합니다.
    *with 명령문으로 생성된 객체 환경 레코드는 Binding Object를 암묵적으로 제공하여 블록문 내에서 프로퍼티에 바로 접근할 수 있습니다.
  // Math.PI -> PI
with (Math) {
  console.log(PI * 2 * 2);
}

const foo = {
	values: [1, 2, 3]
}

function f(foo, values) {
  with (foo) {
    console.log(values);  // Math.PI*2*2 === 12.566370614359172

  }
}

  외부환경에 대한 참조(Reference to the outer environment)

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

1.2 변수 환경(VariableEnvironment)

실행 컨텍스트 안에서 식별자로부터 Environment Records에 바인딩된 것들이 렉시컬 환경(Lexical Environment)입니다.. 환경 변수(Variable Enviroment)는 기본적으로 실행 컨텍스트를 만들 때 렉시컬 환경(Lexical Enviroment)과 같은 값을 가집니다..

민액 렉시컬 환경(Lexical Enviroment)이 with, catch문을 통해 새로운 값을 가지게 되면 코드가 실행될때 변경된 렉시컬 환경(Lexical Enviroment) 컴포넌트에 없는 값을 환경 변수(Variable Enviroment)에서 참조합니다.

  • 환경 변수(Variable Enviroment) 컴포넌트의 값은 스냅샷을 유지하기 때문에 변하지 않습니다.

하지만, ES6에서 한가지 변화가 생기는데, 변수 환경(variable Environment)는 var 키워드의 변수만 저장하고 렉시컬 환경(Lexical Environment)는 함수 선언과 let과 const 키워드로 선언된 변수를 저장합니다.

2. 실행 단계(Creation Phase)

 
  실행 단계에서는 생성단계에 기록되었던 선언문 외 나머지 코드가 순차적으로 실행되는 단계로 식별자에 값 할당이 완료됩니다.

```javascript
consol.log(a) // undifined
consol.log(b) // Referencd error
  
var a = '학생';
let b = '학생';
function study(c, d) {
  reutrn c + d;
}
 
study(10, 10)
  
// 실행 단계에서는 환경 레코드에 식별자의 값을 기록
// { a = '학생' } { b = '학생' }  { study : f {}}

위 코드에서 함수와 let, const 키워드는 렉시컬 환경(Lexical Environment)으로 var 는 변수 환경(VariableEnvironment)으로 key/value로 매핑됩니다.

GlobalExecutionContext = {
	LexicalEnvironment: {
		EnvironmentRecord: {
	      // 식별자 바인딩 위치
			b: < uninitialized >,
			study: < func >
		}
		outer: <null>
		ThisBinding: <Global Object>
	},
	
	VariableEnvironment: {
		EnvironmentRecord: {
	      // 식별자 바인딩 위치
			a: undefined,
		}
		outer: <null>
		ThisBinding: <Global object>
	}
}

실행 단계가 진행도는 동안 변수 할당 수행

GlobalExecutionContext = {
	LexicalEnvironment: {
		EnvironmentRecord: {
	      // 식별자 바인딩 위치
			b: '학생';
			study: < func >
		}
		outer: <null>
		ThisBinding: <Global Object>
	},
	
	VariableEnvironment: {
		EnvironmentRecord: {
	      // 식별자 바인딩 위치
			a: '학생';
		}
		outer: <null>
		ThisBinding: <Global object>
	}
}

study(10, 10)가 호출되면, 새로운 함수 실행 컨텍스트(Function Execution Context)는 함수코드 실행

FUnctionExecutionContext = {
	LexicalEnvironment: {
		EnvironmentRecord: {
	      // 식별자 바인딩 위치
        Arguments: {0: 10, 1: 10, length: 2},
		},
		outer: <GlobalLexicalEnvironment>,
		ThisBinding: <Global Object or undefined>,
	},
     VariableEnvironment: {
	     EnvironmentRecord: {
		},
		outer: <GlobalLexicalEnvironment>,
		ThisBinding: <Global Object or undefined>
	}
}

[Reference]
https://catsbi.oopy.io/fffa6930-ca30-4f7e-88b6-28011fde5867
https://poiemaweb.com/js-execution-context
https://catsbi.oopy.io/fffa6930-ca30-4f7e-88b6-28011fde5867
https://sambalim.tistory.com/m/155

profile
새로운 것을 기록하고 복습하는 공간입니다.

0개의 댓글