자바스크립트는 어떤 실행 컨텍스트가 활성화되는 시점에 다음과 같은 동작 수행
1) 변수를 위로 끌어올림(호이스팅)
2) 외부 환경 정보를 구성
3) this 값을 설정
실행 컨텍스트
: 실행할 코드에 환경 정보들을 모아놓은 객체
1) 동일한 환경에 있는 코드들을 실행할 때 필요한 정보들을 모아 컨텍스트를 구성
2) 이를 콜 스택에 쌓아올림
3) 가장 위에 쌓여있는 컨텍스트와 관련있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서 보장
🧡 실행 컨텍스트를 구성할 수 있는 방법
// (1), (6)
var a = 1;
function outer() {
function inner() {
console.log(a);
var a = 3;
}
inner(); // (3), (4)
console.log(a);
}
outer(); // (2), (5)
console.log(a);
(1) 처음 자바스크립트 코드를 실행하는 순간 전역 컨텍스트가 콜 스택에 담김
(2) outer 함수를 호출하면 자바스크립트 엔진이 outer에 대한 환경 정보를 수집해서 otuer 실행 컨텍스트를 생성한 후 콜 스택에 담음
(3) inner 함수의 실행 컨텍스트가 가장 위에 담기면 outer 컨텍스트와 관련된 코드의 실행을 중단하고 inner 함수 내부의 코드를 순서대로 진행
(4) inner 함수 내부에서 a 변수에 값을 할당하고 나면 inner 함수의 실행이 종료되면서 inner 실행 컨텍스트가 콜 스택에서 제거
(5) a 변수의 값을 출력한 후 outer 함수의 실행이 종료되어 outer 실행 컨텍스트가 콜 스택에서 제거
(6) a 변수의 값을 출력한 후 전역 공간에 더는 실행할 코드가 남아있지 않아 전역 컨텍스트도 제거되고, 콜 스택에는 아무것도 남지 않은 상태로 종료
🧡 실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LexicalEnvironment를 만들고, 이후에는 LExicalEnvrionment를 주로 활용
LexicalEnvironment
"현재 컨텍스트의 내부에는 a, b, c와 같은 식별자들이 있고 그 외부 정보는 D를 참조하도록 구성되어 있다"
environmentRecord
: 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장
ex) 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자 등
호이스팅
: 변수 정보를 수집하는 과정을 모두 마쳤더라도 아직 실행 컨텍스트가 관여할 코드들을 실행되기 전의 상태
=> '자바스크립트 엔진은 식별자들을 최상단으로 끌어올려놓은 다음 실제 코드를 실행한다'라고 생각해도 코드를 해석하는 데 문제x
=> 자바스크립트 엔진이 실제로 끌어올리지는 않지만 편의상 끌어올린 것으로 간주
💛 매개변수와 변수에 대한 호이스팅
function a(x) {
console.log(x);
var x; // (2)
console.log(x);
var x = 2; // (3)
console.log(x);
}
a(1); // (1)
// 호이스팅 된 상태
function a(x) {
var x; // (1)의 변수 선언
var x; // (2)의 변수 선언
var x; // (3)의 변수 선언
var x = 1; // (1)의 할당
console.log(x);
console.log(x);
var x = 2; // (3)의 할당
console.log(x);
}
a();
/* 실행 결과
1
1
2
*/
💛 함수 선언의 호이스팅
function a() {
console.log(b);
var b = 'bbb'; // (2)
console.log(b);
function b() { } // (1)
console.log(b);
}
a();
// 호이스팅 된 상태
function a() {
var b; // 변수는 선언부만 끌어올림
function b() { } // 함수 선언을 전체를 끌어올림
console.log(b);
b = 'bbb'; // (2)의 할당
console.log(b);
console.log(b);
}
a();
/* 실행 결과
f b() { }
'bbb'
'bbb'
*/
// 함수 선언문
function sum(a, b) {
return a + b;
}
// 함수 표현식
var multiply = function (a, b) {
return a * b;
}
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;
}
// 호이스팅 된 상태
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;
};
💙 함수 선언문은 선언 전에 호출해도 아무 문제 없이 실행되므로 혼란을 일으킬 수 있기 때문에 함수 표현식 사용을 더 추천
// (1)
var a = 1;
var outer = function () {
var inner = function () {
console.log(a);
var a = 3;
}
inner(); // (3)
console.log(a);
}
outer(); // (2)
console.log(a);
(1) 전역 컨텍스트
- environmentRecord: { a, outer }
- outerEnvrionmentReference: 아무것도 담기지 않음
- this: 전역 객체
(2) outer 실행 컨텍스트
- environmentRecord: { inner }
- outerEnvrionmentReference: { GLOGBAL, { a, outer } }
=> outer 함수가 선언될 당시(전역 컨텍스트)의 LexicalEnvironment가 담김
- this: 전역 객체
(3) inner 실행 컨텍스트
- environmentRecord: { a }
- outerEnvrionmentReference: { outer, { inner } }
=> inner 함수가 선언될 당시(outer 실행 컨텍스트)의 LexicalEnvironment가 담김
- this: 전역 객체