자바스크립트의 실행 컨텍스트는 실행할 코드에 환경 정보들을 모아놓은 객체입니다.
자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 다음과 같은 일을 합니다.
Last in, first out(LIFO)
마지막으로 들어간 것이 처음으로 나온다.
예시) 바구니에 쌓이는 책. 마지막에 넣은 책이 제일 위에 있어 제일 먼저 꺼낼 수 있다.
First in, first out(FIFO)
처음 들어간 것이 처음으로 나온다.
예시) 원통에 넣는 책. 처음 넣은 책이 아래로 제일 먼저 빠진다.
동일 환경에 있는 코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이것을 스택의 한 종류인 콜스택에 쌓아올립니다.
가장 위에 쌓여있는 컨텍스트와 관련된 코드를 실행하는 방법으로 코드의 환경 및 순서를 보장할 수 있습니다.
var a = 1;
function outer() {
function inner() {
console.log(a); // undefined
var a = 3;
}
inner();
console.log(a); // 1
}
outer();
console.log(a); // 1
// undefined 1 1
코드실행 → 전역(in) → 전역(중단) + outer(in) → outer(중단) + inner(in) → inner(out) + outer(재개) → outer(out) + 전역(재개) → 전역(out) → 코드종료
특정 실행 컨텍스트가 생성되는(활성화되는) 시점은 콜 스택의 맨 위에 쌓이는(노출되는) 순간을 의미합니다.
즉, 현재 실행할 코드에 해당 실행 컨텍스트가 관여하게 되는 시점을 의미합니다.
var a = 3에서 var a를 의미VE와 LE에 담기는 항목은 완벽하게 동일합니다. 스냅샷 유지여부에만 차이점이 있습니다.
VE : 스냅샷(최초의 모습)을 유지합니다.
LE : 스냅샷(최초의 모습)을 유지하지 않습니다. 즉, 실시간으로 변경사항을 반영합니다.
결국, 실행 컨텍스트를 생성할 때, VE에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LE를 만들고 이후에는 주로 LE를 활용합니다.
VE, LE의 구성 요소는 모두 동일하며, "environmentRecord"와 "outerEnvironmentReference"로 구성됩니다.
var a)들이 저장(기록=record)됩니다.매개변수 및 변수는 선언부를 호이스팅 합니다.
function a(x) {
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a(1);
예상 : 1, undefined, 2
function a() {
var x = 1;
console.log(x);
var x;
console.log(x);
var x = 2;
console.log(x);
}
a(1);
function a() {
var x;
var x;
var x;
x = 1;
console.log(x);
console.log(x);
x = 2;
console.log(x);
}
a(1);
실제 : 1, 1, 2
함수 선언은 전체를 호이스팅합니다.
function a () {
console.log(b);
var b = "bbb";
console.log(b);
function b() { }
console.log(b);
}
a();
예상 : 에러(또는 undefined), "bbb", b함수
function a () {
var b; // 변수 선언부 호이스팅
function b() { } // 함수 선언은 전체를 호이스팅
console.log(b);
b = "bbb"; // 변수의 할당부는 원래 자리에
console.log(b);
console.log(b);
}
a();
function a () {
var b; // 변수 선언부 호이스팅
**var b = function b() { } // 함수 선언은 전체를 호이스팅
console.log(b);
b = "bbb"; // 변수의 할당부는 원래 자리에
console.log(b);
console.log(b);
}
a();
실제 : b함수, "bbb", "bbb"
함수명 a가 곧 변수명. function 정의부만 존재, 할당 명령이 없는 경우
function a() {
/* ... */
}
a(); // 실행 O
정의한 function을 별도 변수에 할당하는 경우
변수명 b가 곧 변수명(일반적인 케이스)
var b = function () {
/* ... */
};
b(); // 실행 O
변수명은 c, 함수명은 d. d()는 c() 안에서 재귀적으로 호출될 때만 사용 가능하므로 사용성에 대한 의문이 있습니다.
var c = function d() {
/* ... */
};
c(); // 실행 O
d(); // 에러
console.log(sum(1, 2));
console.log(multiply(3, 4));
// 함수 선언문
function sum(a, b) {
return a + b;
}
// 함수 표현식
var multiply = function (a, b) {
return a + b;
};
LE는 record와 outer를 수집하고 record를 수집하는 과정에서 hoisting합니다.
// 함수 선언문 : 전체를 hoisting
function sum(a, b) {
return a + b;
}
// 함수 표현식 : 변수는 선언부만 hoisting
var multiply; // 변수 선언부
console.log(sum(1, 2));
console.log(multiply(3, 4));
// 변수의 할당부는 원래 자리
multiply = function (a, b) {
return a + b;
};
스코프 체인이 가능토록 하는 것(외부 환경의 참조정보)
var a = 1;
var outer = function () {
var inner = function () {
console.log(a); // undefined
var a = 3;
};
inner();
console.log(a); // 1
};
outer();
console.log(a); // 1
각각의 실행 컨텍스트는 LE 안에 record와 outer를 가지고 있고,
outer 안에는 그 실행 컨텍스트가 선언될 당시의 LE정보가 들어있으니 scope chain에 의해 상위 컨텍스트의 record를 읽어올 수 있습니다.