이전 글에서는 자바스크립트의 var, let, const의 차이와 호이스팅에 대해서 알아봤습니다.
이번 글에선 호이스팅을 설명하려면 빠질 수 없는 내용이며 자바스크립트의 핵심 중 하나인 실행 컨텍스트에 대해 알아보겠습니다.
렛츠고~!
1. 실행 컨텍스트란?
실행 컨텍스트(Execution Context)는 코드를 실행하는 데 필요한 환경 정보들을 담고 있는 객체다.
자바스크립트의 모든 코드는 실행 컨텍스트 안에서 실행되며, 실행 컨텍스트는 호출 스택(Call Stack) 위에 쌓이면서 관리된다.
(호출 스택에 관한 내용은 다음 순서에서 더 자세히 설명하겠습니다.)
자바스크립트 엔진이 코드를 해석하고 실행하는 모든 과정은 실행 컨텍스트를 기반으로 동작함.
2. 실행 컨텍스트의 생성과 흐름
위에서 실행 컨텍스트는 호출 스택 위에 쌓이면서 관리된다고 했는데, 그렇다면 호출 스택이란 뭘까?
호출 스택에 대해 알기 전에 우선 스택에 대해 알아보자.
스택은 자료구조의 일종으로, 한 방향으로만 데이터를 넣고 꺼낼 수 있다는 특성이 있다.
그렇다면 호출 스택은 뭘까?
말 그대로 자바스크립트 호출이 쌓이는 형태로 저장되는 스택을 의미한다.
호출스택은 다음과 같은 동작 과정을 가진다.
예제 코드와 함께 살펴보자.
function foo() {
bar();
}
function bar() {
baz();
}
function baz() {
console.log("Hello");
}
foo();위 코드가 실행되면 다음과 같은 흐름을 가진다.
foo → bar → baz가 순서대로 호출 스택에 쌓인다.baz까지 호출된 뒤에 위에서부터 차례로 제거된다.실행 컨텍스트와 호출 스택 정리
• 이후 함수가 호출될 때마다 각각의 함수 실행 컨텍스트(Function Execution Context) 가 생성되어 호출 스택에 쌓임.
• eval() 같은 예외적인 경우 외에는 함수 호출이 아니면 실행 컨텍스트가 새로 만들어지지 않음.
• 현재 실행 중인 컨텍스트가 끝나면 호출 스택에서 제거되고, 다음 컨텍스트가 실행됨.
3. 실행 컨텍스트의 구성 요소
앞부분에서 실행 컨텍스트를 코드를 실행하는 데 필요한 환경 정보라고 설명했었다.
그렇다면 코드를 실행하는 데 필요한 환경 정보에는 어떤 것들이 있을까?
실행 컨텍스트는 다음의 세 가지 요소로 구성된다.
VariableEnvironment
LexicalEnvironment
ThisBinding
이 중 실제 실행 중에 사용되는 환경 정보인 LexicalEnvironment에 대해 더 자세히 알아보자!
4. LexicalEnvironment의 구조
LexicalEnvironment는 아래 두 가지 요소로 구성된다.
Environment Record
호이스팅에 대한 더 자세한 정보는 이 글을 참조 해주세요.
Outer Environment Reference
5. 스코프 체인
위에서 말한 스코프 체인이란 무엇일까?
스코프 체인이란 한마디로 식별자를 참조할 때 탐색하는 일종의 연결 리스트 구조 이다.
자바스크립트의 x라는 변수가 사용됐다고 가정해보자.
우선 해당 변수가 현재 범위에 있는지 확인해볼 것이다.
그런데 만약 현재 범위에 x라는 변수가 없다면 어떻게 할 것인가?
자바스크립트는 현재 범위의 바깥 범위를 이어서 탐색하게 된다.
예제 코드와 같이 살펴보자.
const x = 1;
function outer() {
const y = 2;
function inner() {
const z = 3;
console.log(x, y, z);
}
inner();
}
outer();호출 스택은 global(전역) → outer → inner 순으로 쌓이게 되며, 변수는 가장 안쪽에서부터 바깥쪽으로 탐색한다.
이에 따라
z는 inner 스코프에서 발견,
y는 outer 스코프에서 발견,
x는 전역 스코프에서 발견한다.
이를 더 자세히 풀어서 설명해보면 다음과 같다.
또 주의깊게 볼 점은, 스코프 체인은 함수가 호출되는 시점이 아닌, 선언된 위치를 기준으로 결정된다는 것이다.
예제 코드와 함께 살펴보자.
const x = 1;
function foo() {
console.log(x);
}
function bar() {
const x = 2;
foo();
}
bar();foo 가 선언된 시점의 OuterEnvironment는 전역 환경이고,
foo가 호출된 시점의 OuterEnvironment는 bar의 LexicalEnvironment다.
만약 스코프 체인이 함수가 호출되는 시점을 기준으로 한다면 콘솔에 2가 찍히겠지만, 실제로는 1이 찍히게 된다.
이를 통해 스코프 체인이 함수 호출 시점 기준으로 동적으로 변경되는 게 아닌
선언 당시의 LexicalEnvironment를 정적으로 참조한다는 것을 알 수 있다.
스코프 체인 정리
6. 스코프의 종류
7. 전역 객체
전역 객체는 자바스크립트 실행 환경에서의 최상위 객체를 의미한다.
전역 객체는 환경별로 이름이 다른데,
브라우저에서는 window, Node.js에서는 global라는 이름을 가지며,
ES2020부터는 환경과 상관없이 전부 globalThis라는 이름으로 통일된 접근이 가능하다.
전역 객체는 전역 변수와 함수의 컨테이너 역할을 하며,
전역 스코프에서 선언된 var와 function 변수들은 전역 객체의 프로퍼티가 된다.
단, let, const, class는 전역 스코프에서 선언해도 전역 객체의 프로퍼티가 되지 않는다.
예제 코드와 함께 살펴보자.
var a = 1;
let b = 2;
console.log(window.a); // 1
console.log(window.b); // undefineda와 b 모두 전역 스코프에서 선언된 변수지만
a는 전역 객체인 window의 프로퍼티가 된 반면, b는 프로퍼티로 들어가지 않은 것을 확인할 수 있다.
번외) 네이티브 객체와 호스트 객체
네이티브 객체는 ECMAScript 명세에서 정의한 표준 객체들로, 실행 환경에 관계없이 항상 동일하게 동작한다.
Array, Object, Promise, Date 같은 우리가 일상적으로 사용하는 대부분의 내장 객체들이 여기에 속한다.
이들은 자바스크립트 엔진 자체가 제공하는 객체이기 때문에 브라우저든 Node.js든 동일한 API를 제공한다.
반면 호스트 객체는 자바스크립트가 실행되는 환경(호스트)이 제공하는 객체들이다.
브라우저에서는 document, XMLHttpRequest, localStorage 같은 DOM/BOM 객체가,
Node.js에서는 fs, http, process 같은 모듈들이 대표적이다.