23장. 실행 컨텍스트
📁 소스코드의 타입
소스코드(실행 가능한 코드)를 4가지 타입으로 구분하는 이유는 소스코드의 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다르기 때문.
🔹 전역 코드
- 전역에 존재하는 소스코드
( 전역에 정의된 함수, 클래스 등의 내부 코드는 포함 X )
- 전역 변수를 관리하기 위해 최상위 스코프인 전역 스코프를 생성해야 함.
var키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수를 전역 객체의 프로퍼티와 메서드로 바인딩하고 참조하기 위해 전역 객체와 연결되어야 함.
- 전역 코드가 평가되면 전역 실행 컨텍스트가 생성됨.
🔹 함수 코드
- 함수 내부에 존재하는 소스코드
( 함수 내부에 중첨된 함수, 클래스 등의 내부코드는 포함 X )
- 지역 스코프를 생성하고 지역 변수, 매개변수, arguments 객체를 관리해야 함.
- 생성한 지역 스코프를 전역 스코프에서 시작하는 스코프 체인의 일원으로 연결해야 함.
- 함수 코드가 평가되면 함수 실행 컨텍스트가 생성됨.
🔹 eval 코드
- 빌트인 전역 함수인
eval 함수에 인수로 전달되어 실행되는 소스코드
strict mode에서 자신만의 독자적인 스코프를 생성.
eval 코드가 평가되면 eval 실행 컨텍스트가 생성됨.
🔹 모듈 코드
- 모듈 내부에 존재하는 소스코드
(모듈 내부의 함수, 클래스 등의 내부 코드는 포함 X)
- 모듈별로 독립적인 모듈 스코프를 생성함.
- 모듈 스코프가 평가되면 모듈 실행 컨텍스트가 생성됨.
📁 소스코드의 평가와 실행
JS엔진은 소스코드를 2개의 과정, "소스코드의 평가"와 "소스코드의 실행" 과정으로 나누어 처리함.
🔹 소스코드의 평가
- 실행 컨텍스트를 생성 -> 변수, 함수 등의 선언문만 먼저 실행 -> 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록.
🔹 소스코드의 실행
- 소스코드 평가 과정이 끝나면, 선언문을 제외한 소스코드가 순차적으로 실행(런타임의 시작)
- 소스코드 실행에 필요한 정보(변수나 함수의 참조)를 실행 컨텍스트가 관리하는 스코프에서 검색해서 취득.
- 변수 값의 변경 등 소스코드의 실행 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록됨.
📁 실행 컨텍스트의 역할
- 소스코드를 실행하는데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역.
- 식별자(변수, 함수, 클래스 덩의 이름)를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 매커니즘.
- 모든 코드는 실행 컨텍스트를 통해 실행되고 관리됨.
- 식별자와 스코프는 렉시컬 환경으로 관리하고 코드 실행 순서는 콜 스택으로 관리
🔹 전역 코드 평가
- 전역 코드의 변수 선언문과 함수 선언문이 먼저 실행됨.
- 선언문 실행에 의해 생성된 전역 변수와 전역 함수가 실행 컨텍스트가 관리하는 전역 스코프에 등록됨.
var키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 객체의 각각 프로퍼티와 메서드가 됨.
🔹 전역 코드 실행
- 런타임이 시작되어 전역 코드가 순차적으로 실행됨.
- 전역 변수에 값이 할당되고 함수가 호출됨.
- 함수가 호출되면 순차적으로 실행되던 전역 코드의 실행을 일시 중단 하고 코드 실행 순서를 변경하여 함수 내부로 진입함.
🔹 함수 코드 평가
- 매개변수와 지역 변수 선언문이 먼저 실행됨.
- 선언문 실행에 의해 생성된 매개변수와 지역 변수가 실행 컨텍스트가 관리하는 지역 스코프에 등록됨.
- 또한 함수 내부에서 지역 변수처럼 사용할 수 있는 arguments 객체가 생성되어 지역 스코프에 등록되고
this 바인딩도 결정됨.
🔹 함수 코드 실행
- 런타임이 시작되어 함수 코드가 순차적으로 실행됨.
- 이때 매개변수와 지역 변수에 값이 할당되고 메서드가 호출됨.
- 함수 코드 실행 과정이 종료되면 함수 호출 이전으로 되돌아가 이어서 전역 코드 실행을 계속함.
※ 코드가 실행되려면 다음과 같이 스코프, 식별자, 코드 실행 순서 등의 관리가 필요
- 선언에 의해 생성된 모든 식별자(변수, 함수, 클래스 등)를 스코프를 구분하여 등록하고 상태 변화(식별자에 바인딩된 값의 변화)를 지속적으로 관리할 수 있어야 함.
- 스코프는 중첩 관계에 의해 스코프 체인을 형성해야 함. 즉, 스코프 체인을 통해 상위 스코프로 이동하여 식별자를 검색할 수 있어야 함.
- 현재 실행 중인 코드의 실행 순서를 변경(함수 호출에 의한 실행 순서 변경)할 수 있어야 하며 다시 되돌아갈 수도 있어야 함.
📁 실행 컨텍스트 스택
- 앞서 다뤘던 것 처럼, JS엔진은 소스코드를 평가(전역 코드 평가, 함수 코드 평가 등)하여 실행 컨텍스트를 생성함.
- 이때, 생성된 실행 컨텍스트들은 스택 자료구조로 관리됨. 이를 실행 컨텍스트 스택(콜 스택)이라고 부름.
- 콜 스택은 코드의 실행 순서를 관리함.
📁 렉시컬 환경
- 식별자와 식별자에 바인딩된 값. 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트임.
- 렉시컬 환경은 스코프와 식별자를 관리
- 렉시컬 환경은 키와 값을 갖는 객체 형태의 스코프(전역, 함수, 블록)를 생성하여 식별자를 키로 등록하고 식별자에 바인딩된 값을 관리.
- 렉시컬 환경은 스코프를 구분하여 식별자를 등록하고 관리하는 저장소 역할을 하는 렉시컬 스코프의 실체임.
📁 실행 컨텍스트의 생성과 식별자 검색 과정
🔹 전역 객체 생성
- 전역 객체는 전역 코드가 평가되기 이전에 생성됨.
- 이때, 전역 객체에는 빌트인 전역 프로퍼티와 빌트인 전역 함수, 그리고 표준 빌트인 객체가 추가되며 동작 환경 또는 특정 환경을 위한 호스트 객체를 포함함.
- 생성된 전역 객체도
Object.prototype을 상속 받음. 즉, 전역 객체도 프로토타입 체인의 일원임.
🔹 전역 코드 평가
- 소스코드가 로드되면 JS엔진은 전역 코드를 평가함.
- 1️⃣ 전역 실행 컨텍스트 생성
- 전역 실행 컨텍스트를 생성하여 콜스택에 푸시함.
- 2️⃣ 전역 렉시컬 환경 생성
- 전역 렉시컬 환경을 생성하고 전역 실행 컨텍스트에 바인딩함.
- 렉시컬 환경은 '환경 레코드'와 '외부 렉시컬 환경에 대한 참조'로 구성됨.
- 3️⃣ 전역 환경 레코드 생성
- 전역 변수를 관리하는 전역 스코프, 전역 객체의 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 제공함.
- 전역 코드에서
this는 전역 객체를 가리키므로 전역 객체가 바인딩됨.
- 객체 환경 레코드:
- 기존
var 키워드로 선언한 전역 변수와 함수 선언문으로 정의한 전역함수, 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 관리.
- 전역 객체의
BindingObject와 연결됨.
(이를 통해 전역 코드 평가 과정에서 선언된 전역변수와 전역함수가 전역 객체의 프로퍼티와 메서드가 됨)
- 선언적 환경 레코드:
let, const 키워드로 선언한 전역 변수를 관리.
- 4️⃣
this 바인딩
- '전역 환경 레코드'의 '[[GlobalThisValue]] 내부 슬롯'에
this가 바인딩됨.
- 5️⃣ 외부 렉시컬 환경에 대한 참조 결정
- 현재 평가 중인 소스코드를 포함하는 외부 소스코드의 렉시컬 환경, 즉 상위 스코프를 가리킴.
- 전역 코드를 포함하는 소스코드는 없으므로
null이 할당됨.
🔹 전역 코드 실행
- 이제 전역 코드가 순차적으로 실행됨.
- 변수 할당문이 있는 경우 변수에 값이 할당됨.
- 식별자 결정을 위해 '실행 중인 실행 컨텍스트'에서 식별자를 검색하기 시작.
- '실행중인 실행 컨텍스트'의 '렉시컬 환경'에서 식별자를 검색할 수 없는 경우, '외부 렉시컬 환경에 대한 참조'가 가리키는 렉시컬 환경, 즉 상위 스코프로 이동하여 식별자를 검색.
🔹 함수 코드 평가
- 함수 호출이 되면, JS엔진은 함수 코드의 평가를 시작함.
- 1️⃣ 함수 실행 컨텍스트 생성
- 함수 실행 컨텍스트를 생성하여 콜스택에 푸시함.
- 2️⃣ 함수 렉시컬 환경 생성
- 함수 렉시컬 환경을 생성하고 함수 실행 컨텍스트에 바인딩함.
- 함수 환경 레코드 생성:
- 매개변수, arguments 객체, 함수 내부에서 선언한 지역 변수와 중첩 함수를 등록하고 관리.
- this 바인딩:
- '함수 환경 레코드'의 [[ThisValue]] 내부 슬롯에
this가 바인딩됨.
- 이 때에
this는 호출 방식에 따라 결정됨.
- 외부 렉시컬 환경에 대한 참조 결정:
- 현재 실행중인 실행 컨텍스트의 렉시컬 환경의 참조가 할당됨.
- 함수 객체의 '내부 슬롯 [[Environment]]'에 현재 실행 중인 실행 컨텍스트의 렉시컬 환경을 저장함.
🔹 함수 코드 실행
- 호출된 함수의 소스코드가 순차적으로 실행됨.
- 매개변수에 인수가 할당되고, 변수 할당문이 실행되어 지역 변수에 값이 할당됨.
🔹 함수 코드 실행 종료
- 콜스택에서 함수 실행 컨텍스트가 팝되어 제거되고, 전역 실행 컨텍스트가 '실행 중인 실행 컨텍스트'가 됨.
🔹 전역 코드 실행 종료
- 더는 실행할 전역 코드가 없으면 전역 코드의 실행이 종료되고 전역 실행 컨텍스트도 콜스택에서 팝되어 콜스택은 비어있게 됨.
📁 실행 컨텍스트와 블록 레벨 스코프
let, const 키워드로 선언한 변수는 모든 코드 블록을 지역 스코프로 인정하는 블록 레벨 스코프를 따름.
- 코드 블록이 실행되면, 다음과 같이 동작하게됨.
- 1️⃣ 블록 렉시컬 환경 생성
- 선언적 환경 레코드:
- 외부 렉시컬 환경에 대한 참조 결정:
- 코드블록이 실행되기 이전의 렉시컬 환경을 가리킴.
- 2️⃣ '현재 실행중인 실행 컨텍스트'의 렉시컬 환경을 교체:
- 코드블록 실행 동안만 생성된 블록 렉시컬 환경으로 바인딩.
- 3️⃣ 코드블록 종료
- 블록 렉시컬 환경의 '외부 렉시컬 환경에 대한 참조'를 통하여 현재 실행중인 실행 컨텍스트'의 렉시컬 환경을 되돌림.