들어가기 전
실행 컨텍스트는 자바스크립트의 동작 원리를 담고 있는 핵심 원리이다.
따라서 실행컨텍스트는 아래 개념들의 동작원리를 담고있다.
- 자바스크립트가 스코프를 기반으로 식별자와 식별자에 바인딩된 값을 관리하는 방식
- 호이스팅이 발생하는 이유
- 클로저의 동작 방식
- 태스큐 큐와 함께 동작하는 이벤트 핸들러와 비동기 처리의 동작 방식
실행 컨텍스트란?
실행 컨텍스트는 소스코드를 실행하는 데 필요한 환경 정보를 모아놓은 객체이다.
즉, 실행 컨텍스트는 변수, 함수, 클래스 등의 이름인 식별자를 등록하고 관리하는 스코프라는 환경 정보를 구성하고, 컨텍스트를 콜 스택에 쌓아올려 가장 위에 있는 컨텍스트를 실행하여 코드의 실행순서를 보장한다.
실행 컨텍스트가 생성되는 시점
자바스크립트 엔진이 스크립트를 처음 실행할 때 전역 컨텍스트를 생성하고 콜스택에 담는다. 이후 함수 호출을 발견하면 해당 함수의 실행 컨텍스트를 콜스택에 담는다. 즉, 함수 실행 컨텍스는 함수가 선언될 때 만들어지는 것이 아닌 호출될 때 만들어지게 된다.
실행 컨텍스트는 1) 전역공간 2) 함수 3) eval() 함수를 호출할 때 만들어지게 되는데,
eval은 속도나 보안이 좋지 않아 거의 쓰지 않는다.
실행 컨텍스트 구조
저장되는 환경 정보들은 아래 사진과 같다.
- Variable Environment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보이다. 선언 시점의 Lexical Environment의 스냅샷으로, 변경사항은 반영되지 않는다.
- Lexical Environment : 처음에는 Variable Environment와 같지만 변경 사항이 실시간으로 반영된다.
- This Binding : this 식별자가 바라봐야 할 대상 객체를 의미한다.
Lexical Environment와 Variable Environment에 담기는 내용은 처음에는 같지만 Variable Environment는 최조 스냅샷을 유지한다는 점에서 차이가 있다. Lexical Environment는 Variable Environment를 그대로 복사해서 만들어지고 이후에 Lexical Environment이 주로 활용된다.
이 두 환경에서는 Environment Record와 Outer Environment Reference로 구성된다.
- Environment Record : 함수 안의 코드가 실행되기 전에 현재 컨텍스트와 관련된 코드의 식별자 정보(매개변수의 이름, 함수 선언, 변수명 등)가 저장된다. 즉 코드가 실행되기 전에 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명을 모두 알고 있게 된다. (호이스팅)
- Outer Environment Reference(외부 렉시컬 환경에 대한 참조) : 상위 스코프를 가리킨다. 즉 현재 environment record보다 바깥에 있는 environment record를 참고한다는 뜻이다. 해당 실행 컨텍스트를 생성한 함수의 바깥 환경을 가리킨다. 먼저 코드 상에서 특정한 변수에 접근하려고 하면 현재 컨텍스트의 Lexical Environment를 탐색하고, 발견되면 그 값을 반환한다. 발견하지 못할 경우 다시 Outer Environment Record에 담긴 Lexical Environment를 탐색하는 과정을 거친다. 전역 컨텍스트의 Lexical Environment까지 탐색해서 해당 변수를 찾을 수 없다면 undefined를 반환한다.
예제로 이해하는 실행 컨텍스트
var a = 1;
function foo () {
const b = 2;
function bar () {
const c = 3;
console.log(a + b + c);
}
bar();
}
foo(); // 6
1. 전역 실행 컨텍스트 생성 및 환경 레코드 생성
위 코드를 실행한다면 전역 컨텍스트가 콜스택에 푸시된다. 전역 컨텍스트는 실행 명령이 따로 없어도 브라우저에서 자동으로 실행하기 때문에 코드가 브라우저에서 로드되는 순간 전역 컨텍스트가 활성화 된다. 전역 코드를 실행시키기 전에 먼저 평가 단계를 거쳐 아래와 같은 컨텍스트가 생성되게 된다.
Environment Record에는 현재 컨텍스트와 관련된 코드의 식별자의 정보가 저장된다고 했다. 컨텍스트를 처음부터 끝까지 훑으면서 순서대로 식별자들을 수집하는데 이때 수집되는 식별자들은 매개변수, 함수, var로 선언된 변수가 해당된다. 따라서 위 사진처럼 foo와 var인 a가 식별자로 등록이 된 것이다. (호이스팅)
var a가 아직 undefined인 이유는 할당 되기 전까지는 초기화 단계만 진행시키기 때문이다.
Lexical Environment
- Environment Record : a = undefined, foo = Function Object
- Outer Environment Reference : null(전역이기 때문에)
2. 전역 코드 실행
콜스택에 전역 컨텍스트와 관련된 코드가 실행되어 Lexical Environment가 업데이트 되고 a의 값이 1로 할당된다. 그리고 foo 함수가 호출된다.
Lexical Environment
- Environment Record : a = 1, foo = Function Object
- Outer Environment Reference : null(전역이기 때문에)
3. foo 함수 실행 컨텍스트 생성 및 환경 레코드 생성
foo함수를 호출하면 자바스크립트 엔진은 foo에 대한 환경 정보를 수집해서 foo 실행 컨텍스트를 생성한 후 콜 스택에 담는다. 이때 foo 함수 실행 컨텍스트는 실행 컨텍스트 스택의 최상위, 즉 실행 중인 실행 컨텍스트가 된다.
Lexical Environment
- Environment Record : b = undefined, bar = Function Object
- Outer Environment Reference : Global Lexical Environment
4. foo()함수 실행
콜스택에 foo() 함수 컨텍스트와 관련된 코드가 실행되어 Lexical Environment가 업데이트 되고 b의 값이 2로 할당된다. 그리고 bar 함수가 호출된다.
Lexical Environment
- Environment Record : b = 2, bar = Function Object
- Outer Environment Reference : Global Lexical Environment
5. bar 함수 실행 컨텍스트 생성 및 환경 레코드 생성
bar함수를 호출하면 자바스크립트 엔진은 bar에 대한 환경 정보를 수집해서 bar 실행 컨텍스트를 생성한 후 콜 스택에 담는다. 이때 bar 함수 실행 컨텍스트는 실행 컨텍스트 스택의 최상위, 즉 실행 중인 실행 컨텍스트가 된다.
Lexical Environment
- Environment Record : c = undefined
- Outer Environment Reference : foo() Lexical environment
6. bar() 함수 실행
콜스택에 bar() 함수 컨텍스트와 관련된 코드가 실행되어 Lexical Environment가 업데이트 되고 c의 값이 3으로 할당된다.
Lexical Environment
- Environment Record : c = undefined
- Outer Environment Reference : foo() Lexical environment
console.log(a + b + c);
1. console 식별자 검색
현재 실행중인 실행 컨텍스트는 bar 함수 실행 컨텍스트이다. console 식별자는 전역 객체에서 객체 환경 레코드의 BindingObject를 통해 찾을 수 있다. 따라서 스코프 체인을 통해 bar, foo, global 순으로 식별자를 찾게 된다.
2. a, b, c 식별자 검색
c 식별자는 bar 컨텍스트 렉시컬 환경에서, b 식별자는 외부 렉시컬 환경에 대한 참조로 이어지는 foo 함수 컨텍스트의 렉시컬 환경에서, a 식별자는 foo에서의 외부 렉시컬 환경에 대한 참조로 이어지는 global 실행 컨텍스트의 렉시컬 환경에서 검색된다.
7. bar 함수 코드 실행 종료
더이상 실행할 코드가 없다면 bar 함수 코드의 실행이 종료된다. 이때 콜스택에서 bar 실행 컨텍스트가 pop 되어 제거되고 foo 실행컨텍스트가 실행중인 실행 컨텍스트가 된다.
콜스택에서 bar함수 컨텍스트가 제거되었다고 해서 bar 함수 렉시컬 환경까지 즉시 소멸하는 것은 아니다. 렉시컬 환경은 실행 컨텍스트에 의해 참조되기는 하지만 독립적인 객체이다. 객체를 포함한 모든 값은 누군가에 의해 참조되지 않을 때 가비지컬렉터(GC)에 의해 메모리 공간의 확보가 해제되어 소멸한다.
bar 함수 실행 컨텍스트가 소멸되었다 하더라도 만약 bar 함수 렉시컬 환경을 누군가 참조하고 있다면 bar함수 렉시컬 환경은 소멸하지 않는다.
8. foo 함수 코드 실행 종료
foo 함수 역시 더이상 실행할 코드가 없으면 foo 함수의 실행이 종료되고 전역 실행 컨텍스트가 실행 컨텍스트가 된다.
9. 전역 코드 실행 종료
전역 코드도 실행할 코드가 없으면 콜스택에서 pop되어 스택에는 아무것도 남아있지 않게 된다.
참고
https://heycoding.tistory.com/86
https://velog.io/@edie_ko/js-execution-context
(책) 모던 자바스크립트 딥다이브
'FE > JavaScript' 카테고리의 다른 글
[JavaScript] 변수의 재할당 과정 (0) | 2024.06.19 |
---|---|
[JavaScript] 자바스크립트 문자열 반복 메소드 repeat() (0) | 2024.04.04 |
[JavaScript] 자바스크립트 최대/최소 정수값 MAX_SAFE_INTEGER/MIN_SAFE_INTEGER (0) | 2024.04.04 |
[JavaScript] 자바스크립트 정수인지 확인하는 Number.isInteger() 메소드 (0) | 2024.04.01 |
[JavaScript] 자바스크립트 대소문자 변경하기 toUpperCase(), toLowerCase() (0) | 2024.03.31 |