1. 실행컨텍스트
- 자바스크립트의 동작 원리를 담고 있는 핵심 개념
- 코드가 실행되기 위해 필요한 환경
1) 소스코드의 타입
- 소스코드의 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다름
소스코드의 타입 | 설명 |
---|
전역코드 | 전역에 존재하는 소스코드 |
함수코드 | 함수 내부에 존재하는 소스코드 |
eval 코드 | 빌트인 전역 함수인 eval 함수에 인수로 전달되어 실행되는 소스코드 |
모듈 코드 | 모듈 내부에 존재하는 소스코드 |
- 전역 코드 → 전역 실행 컨텍스트
- 전역 스코프를 생성
- var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수를 전역 객체의 프로퍼티와 메서드로 바인딩
- 전역 객체와 연결되어야함
- 함수 코드 → 함수 실행 컨텍스트
- 지역 스코프 생성
- 지역 변수, 매개변수, arguments 객체를 관리
- eval 코드 → eval 함수 실행 컨텍스트
- strick mode에서 자신만의 독자적인 스코프 생성
- 모듈 코드
2) 소스코드의 평가와 실행
- 소스코드의 평가 과정
- 실행 컨텍스트를 생성
- 변수, 함수 등의 선언문을 먼저 실행하여 변수나 함수 식별자를 스코프에 등록
- 소스코드의 실행 과정
- 런타임이 시작
- 소스코드 실행에 필요한 정보(변수, 함수 등)를 스코프에서 검색해 사용
- 소스코드의 실행 결과는 다시 스코프에 등록
소스코드가 평가되고 실행되는 과정 예시
const x = 1;
const y = 2;
function foo(a) {
const x = 10;
const y = 20;
console.log(a + x + y);
}
foo(100);
console.log(x + y);
-
전역 코드 평가
- 선언문(변수 선언문, 함수 선언문) 먼저 실행하여 생성된 전역 변수와 전역 함수가 전역 스코프에 등록
-
전역 코드 실행
- 런타임 시작
- 전역 변수에 값의 할당
- 함수의 호출 → 함수 호출 시 전역 코드 실행을 일시 중단하고 함수 내부로 진입
-
함수 코드 평가
- 매개변수와 지역 변수 선언문을 먼저 실행하여 생성된 매개변수와 지역변수가 지역 스코프에 등록
- arguments 객체가 생성되어 지역 스코프에 등록
- this 바인딩을 결정 (함수가 호출되는 방식에 따라 결정)
-
함수 코드 실행
3) 실행 컨텍스트 스택
- 전역 코드는 평가될 때, 함수 코드는 호출될 때 실행 컨텍스트가 생성되고 생성된 실행 컨텍스트는 스택 자료 구조로 관리됨
- 코드의 실행 순서를 관리하는 LIFO(후입 선출)의 구조를 가지는 자료구조
예시
const x = 1;
function foo () {
const y = 2;
function bar () {
const z = 3;
console.log(x + y + z);
}
bar();
}
foo();
2. 실행 컨텍스트 구조
1) 렉시컬 환경 (Lexical Environment)
- 스코프와 식별자를 관리
- 식별자와 특정 변수의 매핑을 지속적으로 트래킹하는 엔진의 내부 구조
- 식별자와 변수의 맵핑이 이루어지는 공간
⇒ 함수 코드가 평가될 때 생성된다
⇒ 렉시컬 환경을 스코프라고 부르는 경우가 많다.
렉시컬 환경의 구성
2개의 컴포넌트로 이루어져 있다.
- 환경 레코드 (Environment Record)
- 현재 실행 컨텍스트 내부의 변수 또는 함수의 선언이 저장되는 공간
- 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소
- 외부 렉시컬 환경에 대한 참조 (Outer Lexical Environment Reference) : 상위 스코프의 렉시컬 환경
(1) 환경 레코드
두 가지 타입이 있다
- 객체 환경 레코드 (Object Environment Record)
- 선언적 환경 레코드 (Declarative Environment Record)
(2) 외부 렉시컬 환경에 대한 참조
- 스코프 체인에 상응하는 개념
- 하위스코프에서 상위스코프의 방향으로만 연결되어 있는 단방향 연결 리스트로 구현되어 있다.
2) 변수 환경 (Variable Environment)
- 렉시컬 환경과 동일
- 차이점
- 렉시컬 환경 : 함수 선언 및 변수(let, const) 바인딩 저장
- 변수 환경 : 변수(var) 바인딩만 저장
3. 실행 컨텍스트 생성 과정
var x = 1;
const y = 2;
function foo (a) {
var x = 3;
const y = 4;
function bar (b) {
const z = 5;
console.log(a + b + x + y + z);
}
bar(10);
}
foo(20);
위 예시 코드에서 실행 컨텍스트가 생성되는 과정은 아래와 같다.
- 전역 객체 생성
- 전역 코드 평가
- 전역 실행 컨텍스트 생성
- 전역 렉시컬 환경 생성
- 전역 환경 레코드 생성
- 객체 환경 레코드 생성
- 선언적 환경 레코드 생성
- this 바인딩
- 외부 렉시컬 환경에 대한 참조 결정
- 전역 코드 실행
- foo 함수 코드 평가
- 함수 실행 컨텍스트 생성
- 함수 렉시컬 환경 생성
- 함수 환경 레코드 생성
- this 바인딩
- 외부 렉시컬 환경에 대한 참조 결정
- foo 함수 코드 실행
- bar 함수 코드 평가
- bar 함수 코드 실행
1) 전역 객체 생성
- 전역 객체는 전역 코드가 평가되기 이전에 생성
2) 전역 코드 평가
최종적으로 생성된 전역 실행 컨텍스트와 렉시컬 환경의 구조
(1) 전역 실행 컨텍스트 생성
- 비어있는 전역 실행 컨텍스트를 생성하고 실행 컨텍스트 스택에 푸시
(2) 전역 렉시컬 환경 생성
- 전역 렉시컬 환경을 생성하고 전역 실행 컨텍스트에 바인딩
-
전역 환경 레코드 생성
- 전역 변수를 관리하는 전역 스코프, 전역 객체의 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 제공
- 객체 환경 레코드 생성
- var 키워드로 선언한 전역 변수와 전역함수, 전역 프로퍼티, 표준 빌트인 객체를 관리
- var 키워드로 선언한 전역 변수와 함수 선언문으로 정의된 전역 함수는 BindingObject를 통해 전역 객체의 프로퍼티와 메서드가 된다.
- 선언적 환경 레코드 생성
- let, const 키워드로 선언한 전역 변수를 관리
-
this 바인딩
-
외부 렉시컬 환경에 대한 참조 결정
- 현재 평가중인 코드는 전역 코드이므로 상위 스코프의 코드는 없다. 따라서 null이 할당된다.
3) 전역 코드 실행
- 순차적으로 코드가 실행되어 변수에 값이 할당된다.
- 식별자를 검색할 때는 현재 실행 컨텍스트에서 식별자를 검색하기 시작하여 외부 렉시컬 환경에 대한 참조가 가리키는 상위 스코프로 이동하여 식별자를 검색한다.
4) foo 함수 코드 평가
- 함수가 호출되면 전역 코드의 실행을 일시 중단하고 함수 내부로 코드 제어권이 이동
최종적으로 생성된 foo 함수 실행 컨텍스트와 렉시컬 환경의 구조
(1) 함수 실행 컨텍스트 생성
- 생성된 함수 실행 컨텍스트는 함수 렉시컬 환경이 완성된 다음 실행 컨텍스트 스택에 푸시
(2) 함수 렉시컬 환경 생성
- foo 함수 렉시컬 환경을 생성하고 foo 함수 실행 컨텍스트에 바인딩
- 함수 환경 레코드 생성
- 매개변수, arguments 객체, 함수 내부에서 선언한 지역 변수와 중첩 함수를 등록하고 관리
- this 바인딩
- 함수 호출 방식에 따라 결정
- foo 함수 내부에서 this를 참조하면 함수 환경 레코드의 [[ThisValue]] 내부 슬롯에 바인딩되어 있는 객체가 반환됨
- 외부 렉시컬 환경에 대한 참조 결정
- foo 함수 정의가 평가된 시점에 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 할당
5) foo 함수 코드 실행
- 런타임이 시작되고 foo 함수의 코드가 순차적으로 실행
- 식별자 결정은 현재 실행 중인 컨텍스트의 렉시컬 환경에서 식별자를 검색하기 시작
6) bar 함수 코드 평가 & 실행
4. 실행 컨텍스트와 블록 레벨 스코프
- 함수 레벨 스코프 : 함수 코드 블록만 지역 스코프로 인정
- 블록 레벨 스코프 : 모든 코드 블록(함수, if문, for 문, while 문, try/catch문 등)을 지역 스코프로 인정
- var 키워드는 함수 레벨 스코프를 따르며, let/const 키워드는 블록 레벨 스코프를 따른다.
블록 레벨 스코프와 렉시컬 환경
let x = 1;
if (true) {
let x = 10;
console.log(x);
}
console.log(x);
- if 문이 실행될 때, let 키워드로 변수 x가 선언되었으므로 블록 레벨 스코프를 생성
- 실행 컨텍스트가 새롭게 생성되는 것이 아니라, 현재 실행 컨텍스트에서 렉시컬 환경을 새롭게 생성하여 기존의 것과 교체한다.
- 반복문에서는 코드 블록이 반복해서 실행될 때마다 코드 블록을 위한 새로운 렉시컬 환경을 생성