늘 겸손하게

JavaScript - 실행 컨텍스트 ( Execution Context ) 본문

Programming/JavaScript

JavaScript - 실행 컨텍스트 ( Execution Context )

besforyou999 2022. 8. 11. 18:15

Execution Context = 실행 컨텍스트

 

실행 컨텍스트 ( Execution Context )

 

 

-> 코드를 실행하는데 필요한 환경을 제공하는 객체

 

환경 : 코드 실행에 영향을 주는 조건이나 상태

 

옛날 ES3 시절에는 함수가 호출될때마다 동적으로 그에 걸맞는 스코프를 생성하고 스코프 체인을 연결해야했으나 ES5 이후 Execution Context를 도입해서 하나의 개념으로 묶어 관리하도록 했음. 이 덕분에 빠르게 식별자를 결정할 수 있게 되었다.

 

그러므로 실행 컨텍스트를 식별자 결정을 더욱 효율적으로 하기 위한 수단이라고 할 수 있고 식별자 결정을 위해 필요한 정보를 한 곳에 모아 제공하는 객체라고 할 수 있다.

 

 

 

자바스크립트 엔진이 코드를 실행하는 순서

 

1. Global Execution Context를 Call Stack에 담는다.

 

2. 함수가 선언되면 Call Stack에 해당 함수의 Execution Context가 쌓인다.

 

3. stack의 맨 위에 존재하는 Execution Context가 활성화된 Execution Context

 

4. 함수 실행이 종료되면 해당 함수의 Execution Context는 Call Stack에서 제거됨

 

5. 코드 실행이 종료되면 Global Execution Context 또한 Call Stack에서 제거됨

 

 

호이스팅 (Hoisting)

 

 

-> 선언문이 마치 최상단에 끌어올려진 듯한 현상

 

< 예시 >

 

console.log(name); // undefined

var name = "Mike";

console.log(name); // Mike

 

다른 언어에서는 불가능하지만 자바스크립트에서는 변수 선언 이전에 변수를 참조하여도 에러가 발생하지 않습니다.

 

이렇게 선언 전에 변수를 참조하여도 오류가 발생하지 않는 현상을 호이스팅(Hoisting)이라고 부릅니다.

 

 

호이스팅 발생 이유

 

자바스크립트 엔진이 먼저 전체 코드를 스캔하면서 변수 같은 정보를 실행 컨텍스트의 Record 부분에 기록해놓기 때문.

 

ECMAScript에서 정의한 정식 명칭은 Environment Record(환경 레코드)로 식별자와 식별자에 바인딩된(연결된) 값을 기록해두는 객체

 

 

생성 단계 - Creation Phase

 

코드를 실행하기전 Execution Context을 생성하고 선언문만 실행해서 Environment Record에 기록하는 단계를 생성 단계라고 합니다.

 

실행 단계 - Execution Phase

 

선언문 외 나머지 코드 순차적 실행. 이때 Environment Record 참조하거나 업데이트

 

 

var 호이스팅 

 

 

생성 단계에서 var로 선언된 변수가 Environment Record에 기록되고 undefined가 바인딩됨. 그러므로 선언 이전에 변수를 참조한 경우 undefined값으로 읽힘

 

< 예시 >

 

console.log(name); // undefined

var name = "Mike";

console.log(name); // Mike

var 변수 선언

 

선언 : var로 변수를 선언하면 메모리 공간을 확보해두고 메모리 주소에 식별자를 연결해둡니다.

초기화 : 초기화 단계에는 식별자에 암묵적으로 undefined값을 바인딩합니다.

 

 

const, let 호이스팅 

 

 

생성 단계에서 const로 선언된 변수가 Environment Record에 기록되나 값이 초기화되지는 않음. 그러므로 선언 이전에 변수를 참조하면 reference 에러가 발생. 이렇게 let 또는 const로 변수를 선언했을 때, 선언 이전에 식별자를 참조할 수 없는 구역을 Temporal Dead Zone(일시적 사각지대)라고 부른다.

 

< 예시 >

 

console.log(name); // Reference Error

const name = "Mike";

console.log(name); // Mike

 

위 코드에서 일시적 사각지대는 1,2번줄이 된다.

 

let, const 변수 선언

 

선언 : let, const로 변수를 선언하면 메모리 공간을 확보해두고 메모리 주소에 식별자를 연결해둡니다.

초기화 : 할당문 직전까지는 변수 초기화되지 않음. 할당문 직전에 변수 호출할 경우 reference error 발생.

 

 

function 호이스팅

 

 

JS에서는 함수를 변수에 담는게 가능

 

/* Global */
study(); // Type Error 발생

var study = () => {
	// do study
};

 

 

위 코드를 실행하면 환경 레코드에 기록되어 있는 study의 값은 undefined이고 undefined는 호출될 수 없기 때문에 Type Error 발생

 

 

/* Global */
study(); // Reference Error

const study = () => {
	// do study
};

 

 

위 코드를 실행하면 환경 레코드에 기록되어 있는 study값은 아직 할당된 값이 없으므로 Reference 에러가 발생

 

이렇게 변수에 함수를 담아서 함수를 선언하는 방식을 함수 표현식(Function Expression)이라고 부릅니다.

 

 

/* Global */
study(); // study 함수 실행됨

function study() {
	// do study
};

 

 

위와 같이 함수 표현식 대신 함수 선언식으로 함수를 선언하는 경우 자바스크립트 엔진은 study 함수의 선언과 동시에 완성된 함수 객체를 생성하여 환경 레코드에 기록됨. 그러므로 아무런 문제없이 study 함수가 실행됨.

 

 

 

Outer

 

 

Outer Environment Reference - 외부 환경 참조

-> 바깥 Lexical Environment를 가리킴

 

Lexical Environment = Record + Outer

한글로는 렉시컬 환경 또는 정적 환경

 

 

let lamp = false;

function goTo2F() {
    let lamp = true;
    console.log(lamp);
}

goTo2F();

 

 

위 코드가 실행될때 Call Stack은 아래와 같은 상황입니다.

 

 

 

 

그러면 goTo2F 함수 내부의 console.log는 어떤 lamp 변수를 참조해야할까요?

 

이러한 상황에서 변수나 함수의 값을 결정하는 것을 식별자 결정( Identifier Resolution )이라고 합니다.

 

 

자바스크립트는 실행 켄텍스트가 Call Stack에 추가되면 이전 렉시컬 환경을 가리키는 Outer를 할당합니다.

 

 

let lamp = false;

function goTo2F() {
    let lamp = true;
    console.log(lamp);
}

goTo2F();

function goTo3F() {
    let pet = 'puppy';
    
    console.log(pet); // puppy
    console.log(corona);
};

 

 

위와 같은 코드를 실행한 경우에 Call Stack은 아래와 같습니다.

 

 

 

함수 goTo3F()가 실행된다고 가정한다면 corona는 어떻게 출력될까요?

 

 

자바스크립트 엔진는 Call Stack 맨 위에 있는 실행 컨텍스트부터 최상위 실행 컨텍스트까지 outer를 통해 corona 변수값을 탐색합니다.

하지만 corona 변수는 선언된적이 없으니 Reference 에러가 발생합니다.

 

 

그러면 corona 대신 lamp를 출력한다고 가정해봅시다.

 

 

function goTo3F() {
    let pet = 'puppy';
    
    console.log(pet); // puppy
    console.log(lamp); // ??
};

 

 

그러면 자바스크립트 엔진은 이전과 같이 Call Stack 맨 위에 있는 실행 컨텍스트부터 최상위 컨텍스트 방향으로 탐색합니다. 하지만 탐색은 2층에서 중단되는데 그 이유는 lamp 변수가 2층 실행 컨텍스트의 환경 레코드에 기록되어 있는것을 보고 탐색을 더 이상하지 않습니다.

 

탐색이 중단되기 때문에 1층의 lamp 변수는 어떤 값이 저장되어있는지는 확인하기가 불가능합니다. 2층 lamp와 1층 lamp가 같은 이름이기 때문에 즉 식별자가 같기 때문에 어떤 값을 가지고 있는지 알 수 없습니다. 

 

이처럼 동일한 식별자로 인해 상위 스코프에서 선언된 식별자의 값이 가려지는 현상을 변수 섀도잉 (Variable Shadowing) 이라고 부릅니다.

 

위와 같은 방식으로 식별자를 결정할때는 outer로 연결된 렉시컬 환경들을 보며 식별자를 결정합니다. 이렇게 식별자를 결정할 때 활용하는 스코프들의 연결리스트를 스코프 체인(Scope chain)이라고 부릅니다.

 

 

출처 

https://www.youtube.com/watch?v=EWfujNzSUmw