
콜 스택(call stack)
- 여러 함수들을 호출하는 스크립트에서 해당 위치를 추적하는 인터프리터
(웹 브라우저의 자바스크립트 인터프리터 같은)를 위한 메커니즘
- 자바스크립트 엔진이 구동되면서 실행 중인 코드를 추적하는 공간
- 함수의 호출을 기록하는 스택(자료구조)
- 현재 어떤 함수가 동작하고있는 지, 그 함수 내에서 어떤 함수가 동작하는 지,
다음에 어떤 함수가 호출되어야하는 지 등을 제어
인터프리터(interpreter)
고급 언어로 작성된 원시코드 명령어들을 한번에 한 줄씩 읽어들여서 실행하는 프로그램
동일한 환경, 하나의 실행 컨텍스트를 구성할 수 있는 방법
- 전역공간(자동 생성)
- eval() 함수
- 함수 (흔히 컨텍스트를 구성하는 방법)
// ------------------------(1)
1 var a = 1;
2 function outer() {
3 function inner() {
4 console.log(a); // undefined
5 var a = 3;
6 }
7 inner(); // -------------(2)
8 console.log(a); // 1
9 }
10 outer(); // ---------------(3)
11 console.log(a); // 1

1. 처음 자바스크립트 코드를 실행하는 순간(1) 전역 컨텍스트가 콜 스택에 담긴다.
2. 전역 컨텍스트와 관련된 코드들을 순차로 진행하다가 (3)에서 outer 함수를 호출하면 자바스크립트 엔진은 outer에 대한 환경 정보를 수집해서 outer 실행 컨텍스트를 생성한 후 콜 스택에 담는다.
3. 전역 컨텍스트와 관련된 코드의 실행을 일시중단하고 outer 실행 컨텍스트와 관련된 코드인 outer 함수 내부의 코드들을 순차로 실행한다.
4. 다시 (2)에서 inner 함수의 실행 컨텍스트가 콜 스택의 가장 위에 담기면 outer 컨텍스트와 관련된 코드의 실행을 중단하고 inner 함수 내부의 코드를 순서대로 진행한다.
5. inner 함수 내부에서 a 변수에 값 3을 할당하고 나면 inner 함수의 실행이 종료되면서 inner 실행 컨텍스트가 콜 스택에서 제거된다.
6. (2)의 다음 줄부터 이어서 실행하고 a 변수의 값을 출력하고 나면 outer 실행 컨텍스트가 콜 스택에서 제거되어 전역 컨텍스트만 남아 있게 된다.
7. (3)의 다음 줄부터 이어서 실행하고 a 변수의 값을 출력하고 나면 전역 공간에 더이상 실행할 코드가 남아 있지 않아 전역 컨텍스트도 제거되고, 콜 스택에는 아무것도 남지 않은 상태로 종료된다.
스냅샷(snapshot)
사진을 찍듯이 특정 시점에 데이터 저장 장치(스토리지)의 파일 시스템을 포착해
별도의 파일이나 이미지로 저장, 보관하는 기술
참고
전역 실행 컨텍스트는 변수 객체를 생성하는 대신 자바스크립트 구동 환경이 별도로 제공하는 객체, 즉, 전역 객체(global object)를 활용한다. 전역 객체에는 브라우저의 window, Node.js의 global 객체 등이 있다. 이는 자바스크립트 내장 객체(native object)가 아닌 호스트 객체(host object)로 분류된다.
호이스팅(hoisting)
변수의 선언과 초기화를 분리한 후, 선언(식별자)만 코드의 최상단으로 끌어올린 것으로 간주하는 가상의 개념(실제로 끌어올리지는 않음)
인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다.
var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화한다.
반면 let과 const로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않는다.
function a (x) { // 수집 대상 1(매개변수)
console.log(x); // (1)
var x; // 수집 대상 2(변수 선언)
console.log(x); // (2)
var x = 2; // 수집 대상 3(변수 선언)
console.log(x); // (3)
}
a(1);
위의 코드는 다음과 같이 해석될 수 있다.
function a () {
var x = 1; // 수집 대상 1(매개변수 선언)
console.log(x); // (1)
var x; // 수집 대상 2(변수 선언)
console.log(x); // (2)
var x = 2; // 수집 대상 3(변수 선언)
console.log(x); // (3)
}
a();
1 function a () {
2 var x; // 수집 대상 1의 변수 선언 부분
3 var x; // 수집 대상 2의 변수 선언 부분
4 var x; // 수집 대상 3의 변수 선언 부분
5
6 x = 1; // 수집 대상 1의 할당 부분
7 console.log(x); // (1)
8 console.log(x); // (2)
9 x = 2; // 수집 대상 3의 할당 부분
10 console.log(x); // (3)
11 }
12 a();


function a () {
console.log(b); // (1)
var b = 'bbb'; // 수집 대상 1(변수 선언)
console.log(b); // (2)
function b () { }; // 수집 대상2(함수 선언)
console.log(b); // (3)
}
a();
function a () {
var b; // 수집 대상 1. 변수는 선언부만 끌어올린다.
function b() { }; // 수집 대상 2. 함수 선언은 전체를 끌어올린다.
console.log(b); // (1)
b = 'bbb'; // 변수의 할당부는 원래 자리에 남겨둔다.
console.log(b); // (2)
console.log(b); // (3)
}
a();
function a () {
var b;
var b = function () { }; // ← 바뀐 부분
console.log(b); // (1)
b = 'bbb';
console.log(b); // (2)
console.log(b); // (3)
}
a();
function a () { /* ... */ } // 함수 선언문, 함수명 a가 곧 변수명
a(); // 실행 OK
var b = function () { /* ... */ } // (익명) 함수 표현식, 변수명 b가 곧 함수명
b(); // 실행 OK
var c = function d () { /* ... */ } // 기명 함수 표현식 변수명은 c, 함수명은 d
c(); // 실행 OK
d(); // Error
주의사항
기명 함수 표현식은 외부에서는 함수명으로 함수를 호출할 수 없다.
함수명은 오직 함수 내부에서만 접근할 수 있다.
console.log(sum(1, 2));
console.log(multiply(3, 4));
function sum (a, b) { // 함수 선언문 sum
return a + b;
}
var multiply = function (a, b) { // 함수 표현식 multiply
return a * b;
}
var sum = function sum (a, b) { // 함수 선언문은 전체를 호이스팅한다.
return a + b;
};
var multiply; // 변수는 선언부만 끌어올린다.
console.log(sum(1, 2));
console.log(multiply(3, 4));
multiply = function (a, b) { // 변수의 할당부는 원래 자리에 남겨둔다.
return a * b;
};
ES5까지의 자바스크립트는 특이하게도 전역공간을 제외하면 오직 함수에 의해서만 스코프가 생성된다. ES6부터는 블록에 의해서도 스코프 경계가 발생하게 함으로써 다른 언어와 훨씬 비슷해졌다. 이러한 블록은 var로 선언한 변수에 대해서는 작용하지 않고, 새로 생긴 let, const, class, strict mode에서의 함수 선언 등에 대해서만 범위로서의 역할을 수행한다. 둘을 구분하기 위해 함수 스코프, 블록 스코프라는 용어를 사용한다.
예시 )
A 함수 내부에 B 함수를 선언하고 다시 B 함수 내부에 C 함수를 선언한 경우, 함수 C의 outerEnvironmentReference는 함수 B의 LexicalEnvironment를 참조한다. 함수 B의 LexicalEnvironment에 있는 outerEnvironmentReference는 다시 함수 B가 선언되던 때(A)의 LexicalEnvironment를 참조한다.
1 var a = 1;
2 var outer = function () {
3 var inner = function () {
4 console.log(a);
5 var a = 3;
6 };
7 inner();
8 console.log(a);
9 };
10 outer();
11 console.log(a);
