- 실행 컨텍스트는 자바스크립트의 동작 원리를 담고 있는 핵심 개념
| 소스코드의 타입 | 설명 |
|---|---|
| 전역 코드 | 전역에 존재하는 소스코드, 함수나 클래스 등의 내부 코드는 포함 X |
| 함수 코드 | 함수 내부에 존재하는 소스코드, 중첩된 함수, 클래스 등의 내부 코드는 포함 X |
| eval 코드 | eval 함수에 인수로 전달되어 실행되는 소스코드 |
| 모듈 코드 | 모듈 내부에 존재하는 소스코드, 모듈 내부의 함수, 클래스 등의 내부코드는 포함 X |
- 구분하는 이유 : 소스코드의 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다르기 때문
- 전역 변수를 관리하기 위해 최상위 스코프인 전역 스코프를 생성
- 호이스팅이 일어나는 var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 객체와 연결
- 전역 코드가 평가되면 전역 실행 컨텍스트 생성
- 지역 스코프를 생성하고 지역 변수, 매개변수, arguments 객체 관리
- 함수 코드가 평가되면 함수 실행 컨텍스트 생성
- strict mode에서 자신만의 독자적인 스코프를 생성
- eval 코드가 평가되면 eval 실행 컨텍스트가 생성
- 모듈별로 독립적인 모듈 스코프를 생성
- 모듈 코드가 평가되면 모듈 실행 컨텍스트가 생
- 자바스크립트 엔진은 소스코드를 **“소스코드의 평가”**와 “소스코드의 실행” 2개의 과정으로 나누어 처리한다.
- 소스코드 평가
- 실행 컨텍스트를 생성
- 변수, 함수 등의 선언문만 먼저 실행
- 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프 등록
- 소스코드 실행 (런타임)
- 변수나 함수의 참조를 실행컨텍스트가 관리하는 스코프에서 검색해서 취득(실행에 필요한 정보를 취득)
- 변수 값의 변경 등 소스 코드의 실행 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록
var x;
x = 1;- 소스코드 평가
- var x를 먼저 실행
- 생성된 변수 식별자 x는 실행 컨텍스트가 관리하는 스코프에 등록되고 undefined로 초기화
- 소스코드 실행
- var x는 소스코드 과정에서 이미 실행 완료된 상태
- x 변수에 값을 할당하려면 먼저 x 변수가 선언된 변수인지 확인 과정 필요
- 실행 컨텍스트가 관리하는 스코프에 x 변수가 등록되어 있는지 확인
- x 변수가 선언된 변수라는 것이 확인이 되면 값을 할당하고 할당 결과를 실행 컨텍스트 등록 후 관리
// 전역 변수 선언
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-
전역 코드 평가
- 선언문만 먼저 실행 ex)
const x,const y,function foo - 전역 스코프에 등록
- 선언문만 먼저 실행 ex)
-
전역 코드 실행
- 전역 코드가 순차적으로 실행 ex)
const x = 1,const y = 2,foo() - 함수가 호출되면 전역 코드의 실행을 일시 중단하고 코드 실행 순서를 변경하여 함수 내부로 진입
- 전역 코드가 순차적으로 실행 ex)
-
함수 코드 평가
- 매개변수와 지역 변수 선언문이 먼저 실행 ex)
const x,const y - 지역 스코프에 등록
- arguments 객체가 생성되어 지역 스코프에 등록되고 this 바인딩도 결정
- 매개변수와 지역 변수 선언문이 먼저 실행 ex)
-
함수 코드 실행
-
함수 코드 순차적 실행 ex)
const x = 10;const y = 20;console.log() -
console.log 메서드를 호출하기 위해 스코프체인을 통해 console을 검색
-
검색하기 위해서는 전역 스코프와 지역 스코프가 연결되어야 함
-
하지만 console은 스코프체인에 등록되어 있지 않음 → 전역 객체(window) 프로퍼티에 존재
⇒ 전역 객체의 프로퍼티가 전역 스코프를 통해 검색 가능해야 한다는 것을 의미
-
console.log 메서드의 실행이 종료되면 함수 호출 이전으로 되돌아가 전역 코드 실행을 계속 한다.
-
-
코드가 실행되려면,
- 스코프를 구분하여 식별자와 바인딩된 값이 관리되어야한다.
- 중첩 관계에 의해 스코프 체인을 형성하여 식별자를 검색할 수 있어야 한다.
- 전역 객체의 프로퍼티도 전역 변수처럼 검색할 수 있어야 한다.
- 현재 실행중인 코드의 실행 순서를 변경할 수 있어야하며 되돌아갈 수 았어야 한다.
❗ 이 모든 것을 관리하는 것이 바로 실행컨텍스트!
- 식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.**
const x = 1;
function foo () {
const y = 2;
function bar () {
const z = 3;
console.log(x + y + z);
}
bar();
}
foo(); // 6⭐ 자바스크립트 엔진은 먼저 전역 코드를 평가하여 전역 실행 컨텍스트를 생성
⭐ 함수가 호출되면 함수 코드를 평가하여 함수 실행 컨텍스트를 생성
→ 생성된 실행컨텍스트는 스택 자료구조(LIFO) 로 관리 ⇒ 실행 컨텍스트 스택
- 전역 코드의 평가와 실행
- 전역 코드 평가 후 전역 실행 컨텍스트를 생성 → 실행 컨텍스트 스택에 푸시
- foo 함수 코드의 평가와 실행
- 전역 코드의 실행은 일시중단, 코드의 제어권이 foo 함수 내부로 이동
- 함수 코드를 평가하여 foo 함수 실행 컨텍스트 생성 → 실행 컨텍스트 스택에 푸시
- bar 함수 코드의 평가와 실행
- 코드 제어권이 bar 함수 내부 이동
- 함수 코드를 평가하여 bar 함수 실행 컨텍스트 생성 → 실행 컨텍스트 스택에 푸시
- foo 함수 코드로 복귀
- bar 함수 종료되면 제어권은 다시 foo 함수로 이동
- 자바스크립트 엔진은 bar 함수 실행 컨텍스트를 제거
- 더 이상 실행할 코드가 없으므로 종료
- 전역 코드로 복귀
- foo 함수 종료되면 제어권은 다시 전역코드로 이동
- foo 함수 실행 컨텍스트를 제거
- 더 이상 실행할 전역 코드가 남지 않으므로 전역 실행 컨텍스트 제거 , 실행 컨텍스트 스택에는 아무것도 남지 않음
❗ 실행 컨텍스트 스택은 코드의 실행 순서를 관리
- 실행 컨텍스트의 스택의 최상위에 존재하는 실행 컨텍스트는 실행 중인 실행 컨텍스트라고 부른다.
- 렉시컬 환경 = 식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트
- 실행 컨텍스트 스택 → 실행 순서 관리
- 렉시컬 환경 → 스코프와 식별자 관리
- 실행 컨텍스트는
LexicalEnvironment 컴포넌트와VariableEnvironment 컴포넌트로 구성 - 생성 초기에는 하나의 동일한 렉시컬 환경을 참조, 이후 몇가지 상황을 만나면 각자가 새로운 렉시컬 환경을 생성하여 내용이 달라지는 경우가 있다.
- 렉시컬 환경은
EnvironmentRecord 컴포넌트와OuterLexicalEnvironmentReference 컴포넌트로 구성 EnvironmentRecord 컴포넌트 (환경 레코드)- 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소
OuterLexicalEnvironmentReference 컴포넌트 (외부 렉시컬 환경에 대한 참조)- 상위 스코프를 가리킨다.
- 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경
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- 전역 객체는 전역 코드가 평가되기 이전에 생성
- 전역 객체도 Object.prototype을 상속 → 전역 객체도 프로토타입 체인의 일원
- 전역 코드 평가 순서
- 전역 실행 컨텍스트 생성
- 전역 렉시컬 환경 생성 2-1. 전역 환경 레코드 생성 2-1-1. 객체 환경 레코드 생성 2-1-2 선언적 환경 레코드 생성 2-2. this 바인딩 2-3. 외부 렉시컬 환경에 대한 참조 결정
- 전역 실행 컨텍스트 생성
- 비어 있는 전역 실행 컨텍스트 생성 → 실행 컨텍스트 스택에 푸시
- 전역 렉시컬 환경 생성
- 전역 렉시컬 환경을 생성 → 전역 실행 컨텍스트에 바인딩
2-1. 전역 환경 레코드 생성
- var 키워드로 선언한 전역 변수와 let, const 키워드로 선언한 전역 변수를 구분하여 관리 필요함 → 전역 환경 레코드는 객체 환경 레코드와 선언적 환경 레코드로 구성
- 객체 환경 레코드는 var키워드로 선언한 전역 변수와 함수 선언문으로 정의한 전역 함수 등을 관리
- 선언적 환경 레코드는 let, const 키워드로 선언한 전역 변수 관리
2-1-1. 객체 환경 레코드 생성
- 객체 환경 레코드는 BindingObject라고 부르는 객체와 연결된다.
- 전역 코드 평가 과정에서 var 키워드로 선언한 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 객체의 프로퍼티와 메서드가 된다.
- 이때 등록된 식별자를 전역 환경 레코드의 객체 환경 레코드에서 검색하면 전역 객체의 프로퍼티를 검색하여 반환 → 식별자(window)없이 프로퍼티를 참조할 수 있는 메커니즘 ex)
window.alert X, alert O
var x = 1
function foo(a){}2-1-2. 선언적 환경 레코드 생성
- let, const 키워드로 선언한 전역 변수는 블록 스코프를 가진다.
- const 키워드로 선언한 변수는 “선언 단계” 와 “초기화 단계”가 분리되어 진행 → 런타임에 실행 흐름이 변수 선언문에 도달하기 전까지 **일시적 사각지대(Temporal Dead Zone: TDZ)**에 빠짐
const y = 2;2.2 this 바인딩
- 전역 코드에서 this를 참조하면 전역 환경 레코드의 내부 슬롯에 바인딩되어 있는 객체가 반환된다. → window
- 객체 환경 레코드와 선언적 환경 레코드에느 this 바인딩이 없다. → this 바인딩은 전역 환경 레코드와 함수 환경 레코드에만 존재
2.3 외부 렉시컬 환경에 대한 참조 결정
- 외부 렉시컬 환경에 대한 참조는 상위 스코프를 말한다.
function foo (a) {
var x = 3;
const y = 4;
function bar (b) {
const z = 5;
console.log(a + b + x + y + z);
}- 전역 코드 평가가 끝난 후 전역 코드 실행 → 변수 x, y에 값이 할당되고 foo 함수 호출
- 선언된 식별자인지 확인하고 어느 스코프의 식별자를 참조하면 되는지 결정이 필요 ⇒ 식별자 결정
- 식별자 결정을 위해 식별자를 검색할 때는 실행 중인 실행 컨텍스트에서 식별자를 검색
- 현재 실행중인 실행 컨텍스트는 전역 실행 컨텍스트이므로 전역 렉시컬 환경에서 식별자 x, y, foo를 검색한다.
- 만약 식별자를 검색할 수 없으면 외부 렉시컬 환경으로 이동하여 검색 → 스코프 체인의 동작 원리
- 하지만 전역 렉시컬 환경은 스코프 체인의 종점이므로 상위 스코프로올라갈 수 없어 식별자 검색에 실패하면 참조에러가 발생한다.
- foo 함수가 호출되면 전역 코드의 실행을 일시 중단하고 foo 함수 내부로 코드 제어권이 이동 → 함수 코드 평가 시작
- 함수 코드 평가 순서
- 함수 실행 컨텍스트 생성
- 함수 렉시컬 환경 생성 2.1 함수 환경 레코드 생성 2.2 this 바인딩 2.3 외부 렉시컬 환경에 대한 참조 결정
-
함수 실행 컨텍스트 생성
- foo 함수 실행 컨텍스트 생성 → 실행 컨텍스트 스택에 푸시
- 이때 foo 함수 실행 컨텍스트는 실행 컨텍스트 스택의 최상위, 실행 중인 실행 컨텍스트가 된다.
-
함수 렉시컬 환경 생성
- foo 함수 렉시컬 환경을 생성
- foo 함수 실행 컨텍스트 바인딩
2.1 함수 환경 레코드 생성
- 함수 환경 레코드는 매개변수, arguments 객체, 함수 내부에서 선언한 지역 변수와 중첩함수를 등록하고 관리
2.2 this 바인딩
- foo 함수는 일반 함수로 호출되었기 때문에 this 는 전역객체(window)를 가리킨다.
2.3 외부 렉시컬 환경에 대한 참조 결정
- foo 함수 정의는 전역 코드 평가 시점에 평가, 이 시점의 실행 중인 실행 컨텍스트는 전역 실행 컨텍스트 → 외부 렉시컬 환경에 대한 참조에는 전역 렉시컬 환경의 참조가 할당
- 자바스크립트는 함수를 어디서 호출했는지가 아니라 어디서 정의했는지에 따라 상위 스코프를 결정
- foo함수가 순차적으로 실행 → x, y에 값이 할당되고 bar 함수 호출
- 식별자 결정을 위해 실행 중인 실행 컨텍스트의 렉시컬 환경에서 식별자를 검색하기 시작
- 모든 식별자가 현재 실행중인 실행 컨텍스트에서 검색할 수 있음 → 검색된 식별자에 값을 바인딩
- bar함수 호출되면 bar 함수 내부로 코드의 제어권이 이동
- 실행 컨텍스트와 렉시컬 환경의 생성과정은 foo 함수 코드 평가와 동일
- bar 함수의 소스코드가 순차적으로 실행 → 매개변수에 인수가 할당, 지역 변수 z에 값 할당
- 그 이후 console.log() 실행
-
console 식별자 검색
- console 식별자를 스코프 체인에서 검색
- 현재 실행 중인 실행 컨텍스트의 렉시컬 환경에서 검색 시작하여 외부 렉시컬 환경에 대한 참조로 이어짐
-
log 메서드 검색
- console 객체의 프로토타입 체인을 통해 메서드 검색
-
표현식 a + b+ x+ y+ z 의 평가
- 평가를 위해 식별자(a,b,x,y,z)를 검색
-
console.log 메서드 호출
- 표현식 a + b+ x+ y+ z가 평가되어 생성한 값을 console.log 메서드에 전달하여 호출
- 실행할 코드가 없으므로 bar 함수 코드의 실행이 종료 → bar 함수 실행 컨텍스트가 실행 컨텍스트 스택에서 제거 → foo 실행 컨텍스트가 실행 중인 실행 컨텍스트가 됨(최상위 스택)
- bar 함수 실행 컨텍스트가 제거되었다고 해서 bar 함수 렉시컬 환경까지 즉시 소멸 X
- 렉시컬 환경은 실행 컨텍스트에 의해 참조되기는 하지만 독립적인 객체
- bar 함수 렉시컬 환경을 누군가 참조하고 있다면 bar 함수 렉시컬 환경은 소멸 X
- 실행 컨텍스트 스택에서 foo 함수 실행 컨텍스트가 제거 → 전역 실행 컨텍스트가 실행 중인 실행 컨텍스트가 된다(최상위 스택)
- 전역 실행 컨텍스트도 실행 컨텍스트에서 제거 → 실행 컨텍스트 스택에는 아무것도 없음
- var 키워드로 선언한 변수는 함수의 코드 블록만 지역 스코프로 인정 → 함수 레벨 스코프
- let, const 키워드로 선언한 변수는 모든 코드 블록을 지역 스코프로 인정 → 블록 레벨 스코프
let x = 1;
if (true) {
let x = 10;
console.log(x); // 10
}
console.log(x); // 1- let 변수 선언 → if 문의 코드 블록을 위한 블록 레벨 스코프 생성
- 선언적 환경 레코드를 갖는 렉시컬 환경을 새롭게 생성 → 기존의 전역 렉시컬 환경을 교체
- 외부 렉시컬 환경에 대한 참조는 if 문이 실행되기 이전의 전역 렉시컬 환경을 가르킴
- 코드 블록이 실행될 때마다 독립적인 렉시컬 환경을 생성하여 식별자의 값을 유지 → 클로저


