이번에는 자바스크립트를 엔진이 동작원리를 담고 있는 자바스크립트의 핵심원리이며, 코드 해석과 디버깅 능력 향상을 위해 실행 컨텍스트에 대해 정리하고자 합니다.
ECMAScript 스펙에 따르면 실행 컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이라 정의합니다. 쉽게 표현하면, 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아 놓은 객체입니다.
소스 코드가 로드되면 자바스크립트 엔진은 전역 코드를 평가하게 되는데 평가 절차는 다음과 같습니다.
자바스크립트 엔진은 다음과 같은 코드 실행에 필요한 여러가지 정보를 미리 파악합니다.
기본 실행 컨텍스트로 함수 내부에 없는 코드는 전역 컨텍스트에서 실행하게 되며, window 객체를 만들고 전역 컨텍스트의 this를 window 객체로 할당합니다.
함수가 호출 될 때마다 해당 함수에 대한 새로운 실행 컨텍스트가 생성되고 각 함수별로 실행 컨텍스트를 가지고 있지만 실행 컨텍스트는 함수가 호출될 때 만들집니다.
eval 함수 내에서 실행되는 코드도 실행 컨텍스트를 가집니다. 하지만, 보안상의 문제로 eval은 잘 사용되지 않습니다.
let a = 0;
function foo() {
let b = 5;
function foo2() {
let c = 3;
}
foo2();
}
foo();
위의 코드를 보면 foo() 함수 내부에 foo2()함수가 선언되어 있습니다. 이 경우 실행 컨택스트의 생성과 소멸 단계는 다음과 같습니다.
컨텍스트 생성 단계에서는 두 가지 일이 발생합니다.
///실행 컨텍스트의 개념
ExecutionContext ={
LexicalEnvironment = <ref. to LexicalEnvironment in memory>,
VariableEnvironment = <ref. to VariableEnvironment in memory>,
}
스코프를 구분하여 식별자를 기록하고 관리한는 저장소 역할을 하며 환경 레코드(Environment Record), 외부환경에 대한 참조(Reference to the outer environment) 두 가지 컴포넌트로 이루어져 있습니다.
렉시컬 환경은 환경 레코드(Environment Record)와 외부환경에 대한 참조(Reference to the outer Environment)로 이루어져 있습니다.
환경 레코드(Environment Record)는 렉시컬 환경안의 함수와 변수 등을 기록합니다.
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)'라고 부릅니다.
// 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
}
}
외부 환경에 대한 참조는 외부에 있는 렉시컬 환경(Lexical Environment)으로 접근할 수 있다는 의미이미, Javascript 엔진은 현재의 렉시컬 환경(Lexical Environment)에서 변수를 찾지 못했다면 외부 환경에서 해당 변수를 찾아 볼 수 있다는 의미입니다.
실행 컨텍스트 안에서 식별자로부터 Environment Records에 바인딩된 것들이 렉시컬 환경(Lexical Environment)입니다.. 환경 변수(Variable Enviroment)는 기본적으로 실행 컨텍스트를 만들 때 렉시컬 환경(Lexical Enviroment)과 같은 값을 가집니다..
민액 렉시컬 환경(Lexical Enviroment)이 with, catch문을 통해 새로운 값을 가지게 되면 코드가 실행될때 변경된 렉시컬 환경(Lexical Enviroment) 컴포넌트에 없는 값을 환경 변수(Variable Enviroment)에서 참조합니다.
하지만, ES6에서 한가지 변화가 생기는데, 변수 환경(variable Environment)는 var 키워드의 변수만 저장하고 렉시컬 환경(Lexical Environment)는 함수 선언과 let과 const 키워드로 선언된 변수를 저장합니다.
실행 단계에서는 생성단계에 기록되었던 선언문 외 나머지 코드가 순차적으로 실행되는 단계로 식별자에 값 할당이 완료됩니다.
```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