[모던 자바스크립트 Deep Dive]23장 실행 컨텍스트
23.1 소스코드의 타입
- ECMAScript 사양은 소스코드를 4가지 타입으로 구분하고, 이는 실행 컨텍스트를 생성
- 소스코드 타입에 따라 실행 컨텍스트 생성 과정과 관리가 달라진다.
1. 전역 코드
- 전역에 존재하는 소스코드
- 전역에 정의된 함수, 클래스 등의 내부 코드는 불포함
- 전역 변수를 관리하기 위해 전역 스코프 생성
- 전역 객체와 연결되어 있음
var
키워드로 선언된 전역 변수, 함수 선언문으로 정의된 전역 함수를 전역 객체의 프로퍼티와 메서드로 바인딩하고 참조해야하기 때문
- 전역 코드가 평가되면 전역 실행 컨텍스트 생성
2. 함수 코드
- 지역 스코프 생성
- 이를 스코프 체인에 연결
- 지역 변수, 매개변수
arguments
객체 관리 - 함수 코드가 평가되면 함수 실행 컨텍스트 생성
3. eval
코드
- strict mode에서 독자적인 스코프를 생성
eval
코드가 평가되면eval
실행 컨텍스트 생성
4. 모듈 코드
- 모듈별로 독립적인 모듈 스코프 생성
- 모듈 코드가 평가되면 모듈 생성 컨텍스트 생성
23.2 소스코드의 평가와 실행
- 소스코드의 평가
- 실행 컨텍스트를 생성
- 변수, 함수 등의 선언문만 먼저 실행
- 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 생성된 변수나 함수 식별자를 키로 등록
- 소스코드의 실행
- 선언문을 제외한 소스코드를 순차적 실행(런타임 시작)
- 소스코드 실행에 필요한 정보를 실행 컨텍스트가 관리하는 스코프에 검색해서 취득
- 변수나 함수의 참조 등
- 소스코드의 실행 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록
- 변수 값의 변경 등
23.3 실행 컨텍스트의 역할
- 스코프를 구분하여 선언에 의해 생성된 모든 식별자를 등록 및 상태 변화 관리
- 스코프 체인을 생성해 이를 통해 식별자 검색
- 실행 중인 코드 실행 순서를 변경하고 되돌아갈 수도 있어야 함
자바스크립트 엔진의 코드 평가 및 실행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 전역 변수 선언
const x = 1;
const y = 2;
// 함수 정의
function foo(a) {
// 지역 변수 선언
const x = 10;
const y = 20;
// 메서드 호출
console.log(a + x + y); // 130
}
// 함수 호출
foo(100);
// 메서드 호출
console.log(x + y); // 3
1. 전역 코드 평가
- 소스코드 평가 과정 실행
- 전역 코드의 변수와 함수 선언문 실행
- 생성된 전역 변수와 전역 함수를 실행 컨텍스트가 관리하는 전역 스코프에 등록
var
키워드로 선언된 전역 변수 ⇒ 전역 객체의 프로퍼티- 함수 선언문으로 정의된 전역 함수 ⇒ 전역 객체의 메서드
2. 전역 코드 실행
- 런타임이 시작되어 전역 코드를 순차적 실행
- 전역 변수에 값을 할당 및 함수 호출
- 함수가 호출되면 전역 코드 실행을 멈추고 코드 실행 순서를 변경해 함수 내부로 진입
3. 함수 코드 평가
- 매개변수와 지역 변수 선언문 먼저 실행
- 생성된 매개변수와 지역 변수를 실행 컨텍스트가 관리하는 지역 스코프에 등록
arguments
객체가 생성되어 지역 스코프에 등록arguments
객체는 함수 내부에서 지역 변수처럼 사용 가능
this
바인딩 결정
4. 함수 코드 실행
- 런타임이 시작되어 함수 코드를 순차적 실행
- 매개변수와 지역 변수에 값 할당
console.log
메서드 호출- 식별자
console
을 스코프 체인을 통해 검색 console
식별자는 전역 객체에 프로퍼티로 존재해 전역 스코프를 통해 검색 가능console
객체의 프로토타입 체인을 통해log
프로퍼티 검색a + x + y
평가- 각 식별자를 스코프 체인을 통해 검색
- 식별자
console.log
메서드 실행 종료
23.4 실행 컨텍스트 스택
- 콜 스택으로도 불리며, 실행 컨텍스트를 스택 자료구조로 관리
- 코드의 실행 순서 관리
- 실행 중인 실행 컨텍스트(running execution context): 언제나 최상위에 존재하는, 현재 실행 중인 코드의 실행 컨텍스트
자바스크립트 엔진의 코드 평가 및 실행
1
2
3
4
5
6
7
8
9
10
11
12
13
const x = 1;
function foo () {
const y = 2;
function bar () {
const z = 3;
console.log(x + y + z);
}
bar();
}
foo(); // 6
1. 전역 코드의 평가와 실행
- 전역 코드를 평가해 전역 실행 컨텍스트 생성
- 전역 실행 컨텍스트에 전역 변수와 전역 함수(
foo
) 등록
- 전역 실행 컨텍스트에 전역 변수와 전역 함수(
- 실행 컨텍스트 스택에 전역 실행 컨텍스트 푸시
- 전역 코드 실행
- 전역 변수에 값이 할당 및 전역 함수(
foo
) 호출
- 전역 변수에 값이 할당 및 전역 함수(
2. foo
함수 코드의 평가와 실행
foo
함수 호출 시 전역 코드 실행 일시 중단- 코드의 제어권이
foo
함수 내부로 이동
- 코드의 제어권이
- 함수 코드를 평가해
**foo
함수 실행 컨텍스트 생성**foo
함수 실행 컨텍스트에 지역 변수와 중첩 함수(bar
) 등록
- 실행 컨텍스트 스택에
foo
함수 실행 컨텍스트 푸시 foo
함수 코드 실행- 지역 변수에 값이 할당되고 중첩 함수(
bar
) 호출
- 지역 변수에 값이 할당되고 중첩 함수(
3. bar
함수 코드의 평가와 실행
bar
함수 호출 시foo
함수 코드 실행 일시 중단- 코드의 제어권이
bar
함수 내부로 이동
- 코드의 제어권이
- 함수 코드를 평가해
**bar
함수 실행 컨텍스트 생성**bar
함수 실행 컨텍스트에 지역 변수등록
- 실행 컨텍스트 스택에
bar
함수 실행 컨텍스트 푸시 bar
함수 코드 실행- 지역 변수에 값이 할당되고
console.log
메서드 호출
- 지역 변수에 값이 할당되고
- 실행 종료
4. foo
함수 코드로 복귀
- 코드의 제어권이
foo
함수로 이동 - 자바스크립트 엔진이
bar
함수 실행 컨텍스트를 실행 컨텍스트 스택에서 팝하여 제거 - 실행 종료
5. 전역 코드로 복귀
- 코드의 제어권이 전역 코드로 이동
- 자바스크립트 엔진이
foo
함수 실행 컨텍스트를 실행 컨텍스트 스택에서 팝하여 제거 - 전역 실행 컨텍스트를 실행 컨텍스트 스택에서 팝하여 제거
23.5 렉시컬 환경
- 식별자와 식별자에 바인딩된 값, 상위 스코프에 대한 참조를 기록하는 자료구조
- 키와 값을 갖는 객체 형태의 스코프(전역, 함수, 블록 스코프) 생성
- 식별자를 키로 등록, 식별자에 바인딩된 값을 관리
- 스코프를 구분하여 식별자를 등록 및 관리
- 실행 컨텍스트를 구성하는 컴포넌트
실행 컨텍스트와 렉시컬 환경
- 실행 컨텍스트(execution context)는
LexicalEnvironment
컴포넌트와VariableEnvironment
컴포넌트로 구성 - 생성 초기
LexicalEnvironment
컴포넌트와VariableEnvironment
컴포넌트는 하나의 동일한 렉시컬 환경 참조
렉시컬 환경의 구성 컴포넌트
- 환경 레코드(EnvironmentRecord)
- 스코프에 포함된 식별자 등록
- 등록된 식별자에 바인딩된 값 관리
- 소스코드 타입에 따라 관리 내용 달라짐
- 외부 렉시컬 환경에 대한 참조(Outer Lexical Environment Reference)
- 상위 스코프(외부 렉시컬 환경)를 가리킴
- 이를 통해 단방향 링크드 리스트인 스코프 체인 구현
23.6 실행 컨텍스트의 생성과 식별자 검색 과정
다음 예제를 통해 실행 컨텍스트의 생성과 식별자 검색 과정을 살펴본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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); // 42
23.6.1 전역 객체 생성
- 전역 코드 평가 이전 생성
- 빌트인 전역 프로퍼티, 빌트인 전역 함수, 표준 빌트인 객체 포함
- 동작 환경에 따라 클라이언트 사이드 Web API 또는 특정 환경을 위한 호스트 객체 포함
Object.prototype
을 상속받기 때문에 프로토타입 체인의 일원이 됨
23.6.2 전역 코드 평가
1. 전역 실행 컨텍스트 생성
- 비어있는 전역 실행 컨텍스트(
Global Exeution Context
) 생성 - 이를 실행 컨텍스트 스택에 푸시
- 이때 실행 중인 실행 컨텍스트는 전역 실행 컨텍스트가 된다.
2. 전역 렉시컬 환경 생성
- 전역 렉시컬 환경(Global Lexical Environment) 생성
- 렉시컬 환경은 환경 레코드와 외부 렉시컬 환경에 대한 참조로 구성
- 이를 전역 실행 컨텍스트에 바인딩
2.1 전역 환경 레코드(Global Environment Record) 생성
- 전역 환경 레코드는 전역 변수를 관리하는 전역 스코프, 전역 객체의 빌트인 전역 프로퍼티, 빌트인 전역 함수, 표준 빌트인 객체 제공
- 즉 전역 스코프와 전역 객체 관리
- 전역 환경 레코드는 객체 환경 레코드와 선언적 환경 레코드로 구성
- 객체 환경 레코드(Object Environment Record)
var
키워드로 선언한 전역 변수, 함수 선언문으로 정의한 전역 함수,빌트인 전역 프로퍼티, 빌트인 전역 함수, 표준 빌트인 객체 관리
- 선언적 환경 레코드(Declarative Environment Record)
let
,const
키워드로 선언한 전역 변수 관리
- 객체 환경 레코드(Object Environment Record)
2.1.1 객체 환경 레코드 생성
- 객체 환경 레코드는
BindingObject
객체와 연결BindingObject
: 전역 객체 생성 단계에서 생성된 전역 객체
BindingObject
객체를 통해var
키워드로 선언한 전역 변수, 함수 선언문으로 정의한 전역 함수는 전역 객체의 프로퍼티와 메서드가 됨- 객체 환경 레코드에서 식별자를 검색하면 전역 객체의 프로퍼티를 검색해 결과 반환
- 따라서
window
식별자를 생략하고 전역 객체의 프로퍼티와 메서드를 사용할 수 있다.
- 따라서
위 예제에서,
x
변수는BindingObject
객체를 통해 전역 객체에 식별자를 키로 등록하고undefined
를 바인딩한다.foo
함수는BindingObject
객체를 통해 전역 객체에 식별자를 키로 등록하고 함수 객체를 즉시 할당한다.
2.1.2 선언적 환경 레코드 생성
위 예제에서,
y
변수는 선언 단계와 초기화 단계 사이, 일시적 사각지대(TDZ)에 빠진다.- 따라서 선언적 환경 레코드에 어떠한 값이 바인딩되어 있지 않다.
2.2 this
바인딩
- 전역 환경 레코드의
[[GlobalThisValue]]
내부 슬롯에this
바인딩- 일반적으로 전역 코드에서는
this
에 전역 객체 바인딩
- 일반적으로 전역 코드에서는
- 전역 코드에서
this
참조 시 전역 환경 레코드의[[GlobalThisValue]]
내부 슬롯에 바인딩된 객체 반환
2.3 외부 렉시컬 환경에 대한 참조 결정
- 외부 렉시컬 환경에 대한 참조: 현재 평가 중인 소스코드를 포함하는 외부 소스코드의 렉시컬 환경, 즉 상위 스코프
- 이를 통해 단방향 링크드 리스트 스코프 체인 구현
- 현재 평가 중인 소스 코드가 전역 코드인 경우, 전역 코드를 포함하는 소스코드는 없으므로
null
할당- 이는 스코프 체인의 종점을 의미
23.6.3 전역 코드 실행
- 변수 할당문, 함수 호출문을 실행하기 위해서는 식별자 결정 과정을 거쳐야 한다.
- 식별자 결정(identifier resolution)
- 어느 스코프의 식별자를 참조할지 결정하는 과정
- 변수 할당문 또는 함수 호출문 실행 전 진행
- 식별자 결정을 위한 식별자 검색
- 실행 중인 실행 컨텍스트에서 시작한다.
- 선언된 식별자라면 렉시컬 환경의 환경 레코드에 등록되어 있다.
- 식별자 검색에 실패하면 외부 렉시컬 환경에 대한 참조가 가리키는 렉시컬 환경에서 식별자를 검색한다.
- 전역 렉시컬 환경에서도 검색할 수 없다면
ReferenceError
를 발생시킨다.
- 실행 중인 실행 컨텍스트에서 시작한다.
23.6.4 foo
함수 코드 평가
foo
함수 호출 시 전역 코드 실행 일시 중단 +foo
함수 내부로 코드 제어권 이동
1. 함수 실행 컨텍스트 생성
foo
함수 실행 컨텍스트 생성- 함수 렉시컬 환경이 완성되면 실행 컨텍스트를 실행 컨텍스트 스택에 푸시
- 실행 중인 실행 컨텍스트는
foo
함수 실행 컨텍스트가 된다.
2. 함수 렉시컬 환경 생성
foo
함수 렉시컬 환경(Global Lexical Environment) 생성- 렉시컬 환경은 환경 레코드와 외부 렉시컬 환경에 대한 참조로 구성
- 이를
foo
함수 실행 컨텍스트에 바인딩
2.1 함수 환경 레코드(Global Environment Record) 생성
- 함수 환경 레코드는 매개변수,
arguments
객체, 함수 내부에서 선언한 지역 변수, 중첩 함수를 등록 및 관리
2.2 this
바인딩
- 함수 환경 레코드의
[[ThisValue]]
내부 슬롯에this
바인딩- 위 예제에선 일반 함수로 호출했으므로 전역 객체를 바인딩
foo
함수에서this
참조 시 함수 환경 레코드의[[ThisValue]]
내부 슬롯에 바인딩된 객체 반환
2.3 외부 렉시컬 환경에 대한 참조 결정
- 외부 렉시컬 환경에 대한 참조에
foo
함수 정의가 평가된 시점에 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조 할당- 위 예제에선 전역 렉시컬 환경의 참조를 할당
- 자바스크립트 엔진은 함수 객체를 생성할 때 현재 실행 중인 실행 컨텍스크의 렉시컬 환경을 함수 객체의 내부 슬롯
[[Environment]]
에 저장 ⇒ 이를 외부 렉시컬 환경에 대한 참조에 할당
23.6.5 foo
함수 코드 실행
- 매개변수에 인수 할당, 변수 할당문 실행, 함수 호출을 위해 식별자 결정 진행
- 식별자 결정을 위해 식별자 검색 진행
- 검색된 식별자에 값을 바인딩
23.6.5 bar
함수 코드 평가
bar
함수 내부로 코드 제어권 이동foo
함수와 동일한 방식으로 코드 평가
23.6.7 bar
함수 코드 실행
- 매개변수에 인수 할당, 변수 할당문 실행, 함수 호출을 위해 식별자 결정 진행
- 식별자 결정을 위해 식별자 검색 진행
- 검색된 식별자에 값을 바인딩
console.log(a + b + x + y + z);
실행
1. console
식별자 실행
- 스코프 체인에서
console
식별자 검색- 스코프 체인: 현재 실행 중인 실행 컨텍스트의 렉시컬 환경에서 시작, 외부 렉시컬 환경 참조로 이루어진 렉시컬 환경의 연속
- 위 예제에서 실행 중인 실행 컨텍스트인
bar
함수 실행 컨텍스트에서 식별자 검색- 없으므로 외부 렉시컬 환경에 대한 참조가 가리키는
foo
함수 렉시컬 환경으로 이동
- 없으므로 외부 렉시컬 환경에 대한 참조가 가리키는
foo
함수 렉시컬 환경에서 식별자 검색- 없으므로 외부 렉시컬 환경에 대한 참조가 가리키는 전역 렉시컬 환경으로 이동
- 전역 렉시컬 환경의 객체 환경 레코드의
BindingObejct
를 통해 전역 객체에서 식별자 찾음
2. log
메서드 검색
console
객체에서log
메서드 검색- 프로토타입 체인을 통해 메서드를 검색한다.
console
객체는 직접log
메서드를 가지고 있다.
3. 표현식 a + b + x + y + z
의 평가
- 표현식을 평가하기 위해 스코프 체인에서
a
,b
,x
,y
,z
식별자를 검색a
,x
,y
⇒foo
함수 렉시컬 환경에서 검색됨b
,z
⇒bar
함수 렉시컬 환경에서 검색됨
4. console.log
메서드 호출
- 표현식이 평가되어 생성된 값 42를 메서드에 전달하여 호출
23.6.8 bar
함수 코드 실행 종료
- 더 이상 실행할 코드가 없으면 코드 실행을 종료
- 실행 컨텍스트 스택에서
bar
함수 실행 컨텍스트를 팝하여 제거- 실행 중인 컨텍스트는
foo
실행 컨텍스트가 된다.
- 실행 중인 컨텍스트는
- 만약
bar
함수 렉시컬 환경을 누군가 참조하고 있다면bar
함수 렉시컬 환경은 소멸하지 않는다.- 렉시컬 환경은 실행 컨텍스트가 참조하는, 독립적인 객체이다.
- 렉시컬 환경 또한 다른 값처럼 참조되지 않을 경우에 가비지 컬렉터가 메모리 공간을 해제해 소멸한다.
23.6.9 foo
함수 코드 실행 종료
- 실행 컨텍스트 스택에서
foo
함수 실행 컨텍스트를 팝하여 제거- 실행 중인 컨텍스트는 전역 실행 컨텍스트가 된다.
23.6.10 전역 코드 실행 종료
- 실행 컨텍스트 스택에서 전역 실행 컨텍스트를 팝하여 제거
- 실행 컨텍스트 스택은 비게 된다.
23.7 실행 컨텍스트와 블록 레벨 스코프
var
키워드로 선언한 변수는 함수 레벨 스코프를 따른다.
let
,const
키워드로 선언한 변수는 블록 레벨 스코프를 따른다.
1
2
3
4
5
6
7
8
let x = 1;
if (true) {
let x = 10;
console.log(x); // 10
}
console.log(x); // 1
if
문의 코드 블록이 실행되면 블록 레벨 스코프를 생성한다.- 선언적 환경 레코드를 갖는 렉시컬 환경을 새로 생성한다.
- 이때 새로 생성된 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는
if
문이 실행되기 이전의 전역 렉시컬 환경을 가리킨다.
- 이때 새로 생성된 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는
- 기존의 전역 렉시컬 환경을 새로 생성된 렉시컬 환경으로 교체한다.
if
문의 실행이 종료되면 코드 블록이 실행되기 이전의 렉시컬 환경으로 되돌린다.- 만약
for
문의 변수 선언문에let
키워드를 사용하면 반복 실행마다 코드 블록을 위한 새로운 렉시컬 환경을 생성한다.
- 만약
This post is licensed under CC BY 4.0 by the author.