November 04, 2021
실행 컨텍스트
는 스코프, 식별자, 코드 실행 순서를 관리하는 영역이다. 스코프와 식별자를 관리하는 렉시컬 환경
과 코드 실행 순서를 관리하는 실행 컨텍스트 스택
으로 이루어져 있다.
렉시컬 환경은 환경 레코드(스코프에 포함된 식별자와 바인딩된 값 관리)와 외부 렉시컬 환경에 대한 참조(상위 스코프)로 구성된다.
실행 컨텍스트
는 자바스크립트의 동작 원리를 담고 있는 핵심 개념이다. 실행 컨텍스트 안에 스코프와 호이스팅, 클로저, this의 개념이 다 들어 있다.
실행 컨텍스트와 호출 스택과 이벤트 루프를 이해하면 자바스크립트 코드를 보고 어떤 순서로 동작할지를 알 수 있다.
소스코드는 평가 과정을 거친 후 실행된다.
평가 과정 때 실행 컨텍스트를 생성하고 실행 컨텍스트 스택에 푸시한다. 이때 변수, 함수 등의 선언문만 먼저 실행(호이스팅)하고, 생성된 식별자를 키로 하여 실행 컨텍스트가 관리하는 렉시컬 환경의 환경 레코드에 등록한다.
평가 과정이 끝난 후 실행 과정(런타임) 때는 선언문을 제외한 소스코드가 순차적으로 실행된다.
소스코드는 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 서로 다르다. 소스코드의 타입은 전역 코드, 함수 코드, eval 코드, 모듈 코드로 총 4가지가 있지만 이 글에서는 전역 코드와 함수 코드에 관해서만 설명하려 한다.
전역 렉시컬 환경 생성 (그리고 실행 컨텍스트에 바인딩)
전역 코드 평가가 완료되면 전역 코드가 순차적으로 실행된다. 실행 도중 함수 호출문을 만나면 함수가 호출되면서 함수 코드의 평가를 시작한다.
변수 할당문 또는 함수 호출문을 실행하려면 먼저 환경 레코드에서 식별자를 검색하여 변수 또는 함수 이름이 선언된 식별자인지를 확인한다. 현재 실행 중인 실행 컨텍스트(환경 레코드)에서 식별자를 검색할 수 없다면 상위 스코프(외부 렉시컬 환경에 대한 참조)로 이동하여 식별자를 검색한다. 이것이 스코프 체인
의 동작 원리이다.
함수 렉시컬 환경 생성 (그리고 실행 컨텍스트에 바인딩)
전역 코드와 함수 코드의 평가 과정에서 다른 곳이 세 군데(*) 있다.
전역 객체는 내용이 많으므로 다른 글에서 설명하기로 하고, 이 글에서는 나머지 두 군데, 객체 환경 레코드와 선언적 환경 레코드에 대해 설명하려 한다.
전역 환경 레코드
는 객체 환경 레코드와 선언적 환경 레코드로 나누어진다. 그 이유는 let과 const의 등장 때문이다. 원래는 전역 객체가 전역 환경 레코드의 역할을 수행했다. 그런데 ES6에 등장한 let과 const로 선언한 변수는 var로 선언한 변수와 달리 전역 객체의 프로퍼티가 되지 않으므로 전역 객체가 아닌 다른 환경 레코드가 필요했다. 그렇기 때문에 선언적 환경 레코드가 생겨난 것이다.
즉, 객체 환경 레코드
는 기존의 전역 객체가 관리하던 var 키워드로 선언한 전역 변수와 함수 선언문으로 정의한 전역 함수, 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 관리하고, 선언적 환경 레코드
는 let, const 키워드로 선언한 전역 변수를 관리한다.
꼭 알아두어야 하는 점이 있다.
실행 컨택스트 스택에서 함수 실행 컨텍스트가 제거되었다고 해서 함수 렉시컬 환경까지 즉시 소멸하는 것은 아니다.
렉시컬 환경은 실행 컨텍스트에 의해 참조되기는 하지만 독립적인 객체이다. 객체를 포함한 모든 값은 누군가에 의해 참조되지 않을 때 비로소 가비지 컬렉터에 의해 메모리 공간의 확보가 해제되어 소멸한다. 가비지 컬렉터는 누군가가 참조하고 있는 메모리 공간을 함부로 해제하지 않는다. 이는 클로저를 관통하는 원리이다.
글만으로는 이해하기 힘들므로 모던 자바스크립트 Deep Dive 23.7 실행 컨텍스트와 블록 스코프를 보며 그림과 함께 이해하는 것을 추천한다.
let과 const 키워드로 선언한 변수를 포함한 코드 블록이 실행되면 블록 레벨 스코프를 생성해야 한다. 이를 위해 선언적 환경 레코드를 갖는 렉시컬 환경을 새롭게 생성하여 기존의 렉시컬 환경을 교체한다. 이때 새롭게 생성된 코드 블록을 위한 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는 코드 블록이 실행되기 이전의 렉시컬 환경을 가리킨다.
코드 블록의 실행이 종료되면 코드 블록이 실행되기 이전의 렉시컬 환경으로 되돌린다.
for문의 변수 선언문에 let 키워드를 사용한 for문은 코드 블록이 반복해서 실행될 때마다 코드 블록을 위한 새로운 렉시컬 환경을 생성한다. 만약 for문의 코드 블록 내에서 정의된 함수가 있다면 이 함수의 상위 스코프는 for문의 코드 블록이 생성한 렉시컬 환경이다.