만약 누군가에게 자바스크립트에 대해 한 가지만 알려줄 수 있다면 실행 컨텍스트에 대해 알려줄 것 같다.
그만큼 실행 컨텍스트는 가장 중요한 자바스크립트의 핵심 원리이자 여러 가지 특이한 특징들을 가지는 이유가 된다.
먼저 실행 컨텍스트를 한 줄로 요약해 보자면 자바스크립트가 실행됐을 때 스택형태로 쌓이는 하나의 객체이다.
이것만 들어서는 이해하기 힘들 것 같으니 사진과 함께 설명하자면
function foo() {
function bar() {
console.log('bar')
}
bar()
}
foo()
위 코드를 실행했을 때 아래 사진처럼 진행되게 된다
1. 먼저 전역 실행컨텍스트가 쌓이게 되고
2. 그 이후 foo가 실행되며 스택에 쌓임
3. foo안에 있는 bar가 실행되고 스택에 쌓임
4. bar 실행 종료되며 스택에서 빠져나옴
5. foo 실행 종료되며 스택에서 빠져나옴
실행 컨텍스트의 요소
그러면 이 실행 컨텍스트는 어떤 정보를 가지고 있는 객체일까?
당연히 코드 실행에 필요한 정보들을 가지고 있는다.
보다 자세히 서술하자면 실행 컨텍스트는 3가지 요소들을 가지고 있게 된다.
ThisBinding
먼저 실행 컨텍스트는 this binding에 대해 가지고 있는다.
이 요소만 가지고도 쓸 내용들이 매우 많다 그렇기 때문에 이 내용은 다른 글에서 다뤄볼까 한다.
다른 언어를 배웠던 사람이면 아니 그냥 자신 인스턴스를 가리키는 거 아닐까 착각할 수 있겠지만,
이 문법은 예상치 못한 다양한 버그를 일으키기도 하고 잘 활용한다면 다양한 편법을 쓸 수 있게 해 준다.
그렇기 때문에 다른 글에서 자세히 다뤄볼까 한다..
일단 일반적인 경우에는 this는 전역 객체를 가리킨다는 사실만 안채로 우선 스킵..
Lexical Environment (렉시컬 환경)
렉시컬 환경에는 코드 실행을 위한 두 가지 요소들이 들어가게 된다. 여기에는 변경 사항들이 실시간으로 반영되게 된다.
1. environmentRecord
첫 번째 요소는 environmentRecord로 해당 실행 컨텍스트에 선언되어 있는 변수, 함수들이 모여있는 요소이다.
함수가 실행될 때 함수에 선언되어 있는 변수, 함수들을 먼저 모아두기 때문에 호이스팅이 발생한다.
function foo(a) {
const b = 'b'
function bar() {
console.log(a + b)
}
bar()
}
foo('a') // ab
다시 실제 코드를 통해 보면 foo라는 함수가 실행될 때 아래 값들은 environmentRecord에 담기게 된다
1. 매개변수 a
2. 함수 내부에서 선언한 b
3. 함수 내부에서 선언한 함수인 bar
이렇게 함수 내부에서 쓸 값들을 environmentRecord에 담은 후 코드 실행을 이어간다.
별거 없는데? 라고 생각할 수 있겠지만 다음 글에서 소개할 호이스팅에 대해 듣게 되면 약간 어지러울 수 있다.
2. outer-EnvironmentReference
두 번째 요소는 outer-EnvironmentReference이다.
이 요소에는 함수 외부에 있는 값들에 대한 참조를 담게 된다. 다시 한번 아까 코드를 통해 설명해 보자면
function foo(a) {
const b = 'b'
function bar() {
console.log(a + b)
}
bar()
}
foo('a') // ab
함수 bar의 environmentRecord에는 분명 a, b 정보 모두 없다.
그렇다면 이 친구는 도대체 어떻게 ab라는 콘솔로그를 찍을 수 있었던 걸까?
바로 여기서 outer-EnvironmentReference가 쓰이게 된다.
bar가 실행될 때 outer-EnvironmentReference에는 (foo, {a: 'a', b: 'b'}) 이런 식으로
외부 함수 + 그 함수의 environmentRecord을 저장하게 된다
그다음 어떤 변수를 쓰려고 한다면 자신의 environmentRecord부터 찾은 이후 없다면 outer-EnvironmentReference을 통해 하나씩 바깥으로 나가며 이 변수를 찾으려고 한다 이게 바로 스코프 체인이다.
Variable Environment
마지막 요소는 Variable Environment이다.
이 값은 Lexical Environment와 같은 요소를 가지게 된다.
하지만 이 값은 함수 실행 당시의 스냅샷으로 변경사항들이 반영되지 않는다.
함수 내에서 렉시컬 환경에 변화를 만드는 문법이 두 가지 있다
이 문법은 바로 catch, with 이다.
catch를 통해 간단히 예시를 보여주자면
function foo() {
var a = 1;
try {
console.log(e); // (ReferenceError: e is not defined)
} catch (e) {
console.log(e); // 굿
console.log(a); // 1
var a = 3;
}
}
foo();
try에서 e를 사용했을 때 undefined이 찍히지 않는다.
즉, e는 foo가 실행될 때 렉시컬 환경에 포함되지 않는다는 의미이다.
반면 catch문에서는 e를 호출했을 때 문제가 생기지 않는다.
catch가 발생했을 때 렉시컬 환경이 변화되었다는 의미이다.
하지만 a를 호출했을 때 undefined이 아닌 1이 로그에 찍히고 있다..
분명 렉시컬 환경은 새로운 a, e가 environmentRecord로 들어가도록 변화되었지만 a는 외부에서 선언 및 할당한 1을 호출하고 있다.
이때 초기 스냅샷인 VariableEnvironment를 참조한다고 한다.
이렇게 실행 컨텍스트는 3가지 요소들로 이루어져 자바스크립트 코드의 실행을 돕는다.
분명 글에서는 실행 컨텍스트만 다루었지만, 자바스크립트의 특징으로 꼽히는 렉시컬 환경, 스코프 체인, 호이스팅과 같은 내용들이 전부 튀어나왔다.
실행컨텍스트는 자바스크립트의 여러가지 특징들을 야기시키는 원리로 자바스크립트의 많은 부분을 이해할 수 있게 도와주는 핵심 원리이기 때문에 꼭 자바스크립트를 사용할 계획이라면 이해하고 사용하는 것이 좋을 것 같다.
'front-end > javascript' 카테고리의 다른 글
호이스팅이란? (0) | 2023.01.23 |
---|