execution context & stack frame
- 자바스크립트가 작동하는 과정을 공부하다 보면 call stack 이 나오며 stack 자료구조는 모두 알다시피 FILO(First-In-Last-Out)을 따릅니다. call stack 에서 코드의 실행이 이루어지며 FILO 를 통해 함수의 실행 순서를 도중에 잃어버리지 않고 유지하며 작업을 수행할 수 있습니다.
- 이를 이해하기 위해서는 execution context 와 frame 의 개념을 이해해야 하며 이를 이해하면 디버깅, 최적화 그리고 일반적인 퍼포먼스 위험을 줄이는데 도움을 줍니다. 더 나아가 클로저와 이벤트 루프를 이해하는데 필요한 개념입니다.
EC (execution context)
- JavaScript 를 실행하면 엔진은 코드를 읽으며 스크립트 파일을 스캔하는데 이때 코드의 실행을 처리하는 EC (execution context, 실행 컨텍스트) 라는 환경을 만듭니다.
- EC 는 코드가 실행되는 환경의 추상적이고 개념적인 표현 이지만 물리적으로 객체를 가집니다. 이는 아래에서 계속 설명하겠습니다.
EC 의 2가지 단계
- EC 는
creation phase
와 execution phase
두 단계로 이루어 지며 이는 거의 동시에 일어나 정확히 구별되지는 않습니다. 하지만 이를 나누어 이해하면 JS 엔진이 코드를 처리하는 방식을 이해하는데 더 도움을 줍니다.
creation phase
: 이 단계에서 JS 의 엔진은 EC 를 생성하며, scope chain 초기화, 변수와 함수 선언, 그리고 this
객체를 생성합니다. 변수 선언이 undefined
으로 초기화 되는 과정 또한 이 단계에서 이루어집니다.
execution phase
: 이 단계에서 JS 의 엔진은 코드를 한줄씩 실행합니다. 스크립트의 문이나 표현을 처리하고 함수를 호출하며 변수에 값을 할당합니다. 위의 creation phase
에서 선언된 변수나 함수는 이 단계에서 사용할 수 있습니다.
EC 의 2가지 종류
- EC 는 두가지 종류가 있습니다.
global execution context
: 맨 처음 JavaScript 파일이 실행될 때 생성되며 global scope 의 범위 내부에 정의된 모든 변수와 함수를 포함합니다. 또한 GEC
는 유일하며 최상위에 위치합니다.
function execution context
: 함수가 호출될 때 마다 매번 생성되며 각 함수의 local scope 는 함수의 로컬 범위를 나타내며 함수 내부에 정의된 모든 변수와 함수를 포함합니다.
EC 생성 조건
- EC 가 새롭게 생성되는 조건은 아래와 같습니다.
- EC 는 함수가 호출될때 생성됩니다. 여기서 함수란 user-defined 함수 즉, 일반적으로 개발자가 새롭게 만드는 함수를 의미합니다.
- 단순히 변수를 선언하거나 연산자를 사용할 때에는 새로운 EC 가 생성되지 않습니다 (기존에 있던 EC 에서 수행됩니다).
- 몇몇 built-in 함수 (대표적으로 console.log()) 는 새로운 EC 를 생성하지 않습니다. 대신에 기존에 있던 EC 에서 수행됩니다.
EC 구성

- EC 는 추상적이며 개념적인 표현 방식이지만 실제로 객체로 나타내어 지며 call stack 에 푸쉬 되어 들어갑니다.
EC
는 위처럼 구성되어 있으며 GEC
의 variable object (VO)
는 global object (GO)
를, FEC
의 variable object (VO)
는 activation object (AO)
를 가리킵니다.
variable object (VO)
: 실행 컨텍스트가 생성될 때, JS 엔진은 실행에 사용하는 여러 정보를 담을 객체를 생성하는데 이를 VO 라고 합니다. 현재 컨텍스트에 있는 모든 변수와 함수의 선언을 저장하는 특수 객체 입니다.
scope chain
: list 형 자료구조로 variable object(VO)
와 activation object (AO)
로 구성되어 있습니다. scope chain
의 첫번째 객체는 GEC
의 경우 GO
를 가리키고, FEC
의 경우 해당 AO
를 가리킵니다. JS 엔진은 첫번째 객체부터 변수 탐색을 시작하며 변수가 발견되거나 전역 범위에 도달할 때 까지 체인을 다음 범위로 이동하며 탐색합니다.
this binding
: this
값이 할당됩니다. this
값은 함수가 어떻게 호출되는지에 달려 있으며 현재 실행 컨텍스트의 this
값을 정의합니다.
Global Execution Context (GEC) 구성
variable object (VO)
GEC
의 VO 는 유일하며 global object (GO)
를 가리킵니다.
- VO 는 현재 범위에서 선언된 모든 변수 및 함수를 포함합니다.
GO
를 가리키는 이유는 GEC
는 전역범위에 위치하는 EC 이기 때문에 함수의 매개변수가 없기 때문입니다. 따라서 GO
는 전역변수와 전역함수를 포함하며, 매겨변수는 포함하지 않습니다.
global object (GO)
는 브라우저의 경우 window 객체를 의미하며 node.js 의 경우 global
객체를 의미합니다.
GO
는 스크립트 어디에서든 접근 가능한 다양한 build-in 속성과 메소드를 제공하며 대표적으로 console
setTimeout
Math
등이 있습니다.
scope chain
GEC
에서 scope chain
은 한개의 원소만 가지고 있으며 이는 GO
입니다. 따라서 GEC
에서 scope chain
의 첫번째 객체는 GO
를 가리키며 이는 VO
가 가리키는 GO
와 일치합니다.
this binding
Function Execution Context (FEC) 구성
variable object (VO)
FEC
에서 VO 는 activation object (AO)
를 가리킵니다.
activation object (AO)
는 함수가 호출될 때 마다 생성되며 지역변수 그리고 함수를 포함합니다. 또한 함수가 완전히 종료되면 사라집니다.
AO
는 함수 외부에서 접근 할 수 없습니다.
argument object
: activation object (AO)
의 속성중 하나로 따라서 GEC
에는 없고 FEC
에만 존재합니다. 함수에 전달된 모든 매개변수를 포함하는 배열과 같은 객체입니다. 모든 함수에 대해 자동으로 생성되며, 인수의 인덱스 또는 길이 속성을 사용하여 속성에 액세스할 수 있습니다.
scope chain
- 새로운 함수가 매번 실행 될 때 마다 새로운
AO
가 생성되며 이는 새로운 scope hain
의 맨 앞에 추가되고 그 뒤를 기존에 존재하던 scope chain
이 복사됩니다.
- 따라서 현재 범위의
scope chain
에서 첫번째 원소가 가리키는 객체는 해당 범위의 VO
가 가리키는 AO
와 항상 동일합니다. 또한 기존의 scope chain
이 뒤따르므로 마지막 객체는 항상 GO
를 가리킵니다.
this binding
activation object vs argument object
| activation object | argument object |
---|
정의 | 함수가 호출될 때 마다 매번 생성되는 특별한 개체로, 함수의 모든 지역변수 선언, 내부 함수 선언, 그리고 this 값을 가지고 있습니다. | 모든 배개변수를 가지고 있는 list 형 자료구조 |
접근 | 바로 접근 불가능 하며, 함수의 scope chain 을 통해 접근 가능합니다. | arguments 키워드를 통해 바로 접근 가능합니다. |
변동성 | mutable, 지역 변수는 새롭게 추가, 수정 그리고 삭제 될 수 있습니다. | immutable, 수정 불가능합니다. |
GEC VO vs FEC VO
| VO in GEC | VO in FEC |
---|
Points to | Global Object (GO) | 현재 함수의 Activation Object (AO) |
Contains | 전역 변수 선언 및 함수 선언 | 현재 함수에 전달된 매개변수와 함수 내에서 선언된 모든 지역 변수 선언 |
생성 시기 | 전역 범위로 들어갈 때 | 함수가 호출될 때 |
사용 시기 | 변수와 함수를 찾을 때 | 변수, 함수 그리고 매개변수를 찾을 때 |
접근 가능 범위 | 코드 어디서나 가능 | 현재 함수 내부에서만 가능 |
EC 구성의 생성 순서
variable object
-> scope chain
-> this
순으로 생성됩니다.
작동 과정
function myFunction2() {
console.log('A new FEC has been created by myFunction2.');
console.log('FEC2 this:', this);
}
function myFunction1() {
console.log('A new FEC has been created by myFunction1.');
console.log('FEC1 this:', this, '\n');
myFunction2.call(myFunction2);
}
console.log('GEC this:', this, '\n');
myFunction1();
- 위 코드를 실행하면 call stack 에서 이를 처리하는 과정은 아래와 같습니다.

- 새로운 EC 가 생성될 때 마다
call stack
의 top
에 추가되며 회색으로 칠한 부분은 control (제어권) 을 가지고 있음을 나타냅니다.
- 맨 처음
GEC
가 추가되며 myFunction1
과 myFunction2
가 추가될 때 마다 새로운 execution context 가 call stack
의 탑에 추가됩니다.
- 호출된 역순으로 함수가 완료되며
call stack
에서 또한 역순으로 사라집니다.
- 이를 위에서 소개한 EC 구성을 포함해 설명하면 그 과정은 아래 그림과 같습니다.

- 아래는 온라인 JS 인터프리터로 위 코드를 실행하면 나오는 결과입니다.
- this 바인딩이 가리키는 객체는 인터프리터나 환경에 따라 다르지만 위의 경우 다음과 같습니다.
- GEC에서 this 바인딩이 가리키는 객체는 아무것도 가리키지 않습니다.
- 저는 온라인 환경에서 실행하였고 이경우 this 바인딩이 GEC에서 아무것도 제공되지 않는것 처럼 보입니다.
- FEC1에서 this 바인딩이 가리키는 객체는
global object
입니다.
- FEC2에서 this 바인딩이 가리키는 객체는 해당 EC의
activation object
입니다.
- myFunction2 를 호출할 때 call 메소드를 사용하면 인자로 주어지는 객체를 가리키게 됩니다. 따라서 FEC2는 myFunction2 를 가리킵니다.
GEC this: {}
A new FEC has been created by myFunction1.
FEC1 this: Object [global] {
global: [Circular],
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Function]
},
queueMicrotask: [Function: queueMicrotask],
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Function]
}
}
A new FEC has been created by myFunction2.
FEC2 this: [Function: myFunction2]
- 위의 코드를 예제로 들었을 경우,
call stack
에는 총 3개의 EC
가 push 되며, 제일 먼저 GEC
그 후 두개의FEC
가 들어옵니다.
- 첫번째는
GEC
전역 실행 컨텍스트 입니다.
variable object(VO)
은 global object(GO)
를 가리킵니다.
scope chain
의 원소 갯수는 하나이며 global object(GO)
를 가리킵니다.
this binding
은 global object(GO)
를 가리킵니다.
- 두번째는
FEC
함수 실행 컨텍스트 입니다. myFunction1
함수의 호출로 인해 생성됩니다.
variable object(VO)
는 해당 함수의 activation object(AO)
를 가리킵니다.
scope chain
의 전체 원소 갯수는 2개이며 첫번째 원소는 해당 함수의 AO
를 가리키고 두번째 원소는 GO
를 가리킵니다.
this binding
은 FEC1에서 명시적으로 바인딩 되지 않았기 때문에 global object(GO)
를 가리킵니다.
- 세번째는
FEC
함수 실행 컨텍스트 입니다. myFunction2
함수의 호출로 인해 생성됩니다.
variable object(VO)
는 해당 함수의 activation object(AO)
를 가리킵니다.
scope chain
의 전체 원소 갯수는 3개이며 첫번째 원소는 해당 함수의 AO
를 가리키고 두번째 원소는 FEC1의 AO
를 가리킵니다. 마지막으로 3번째 원소는 GO
를 가리킵니다.
this binding
은 FEC1에서 myFunction2를 호출할 때 call 메소드를 사용해 myFunction2 를 인자로 넘겨주었기 때문에 FEC2의 AO
를 가리킵니다.
주의할 점
- GO이든 AO이든 주의할 점은 두가지 모두 변수 선언 과 함수 선언에 관한 정보를 가지고 있는것입니다.
- 변수의 초기화 값과 함수의 구현부는 이곳에 저장되지 않습니다.
- AO의 argument object 는 매개변수를 가지고 있는것이지 변수의 초기화 값을 포함한다는 의미가 아닙니다.
References