동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성
구성된 컨텍스트들을 차례대로 콜 스택에 쌓아 올렸다가
가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장합니다.
스택(Stack)이란?
Last In First Out 라고도 하며, 나중에 쌓인 데이터부터 먼저 처리되는 방식을 말합니다.
실행 순서 A -> B -> C -> D -> E
이미지 출처 : 네이버 블로그 : 자바로배우는 알고리즘 기초
// ------------------- (1)
var a = 1;
function outer() {
function inner() {
console.log(a);
var a = 3;
// --------------- (2)
}
inner(); // -------- (3)
console.log(a);
// ----------------- (4)
}
outer(); // ---------- (5)
console.log(a);
// ------------------- (6)
- 자바스크립트 코드가 실행하는 순간 전역 컨텍스트가 콜 스택에 쌓임
전역 컨텍스트라는 개념은 일반적인 실행 컨텍스트와 특별히 다를것이 없고, 자바스크립트 파일이 열리는 순간 활성화 됨- 차근차근 코드를 실행하다가 outer 함수를 호출하면서 outer에 대한 환경 정보를 수집해서 outer 실행 컨텍스트를 생성한 뒤 콜 스택에 담습니다.
콜 스택 맨 위에 outer 실행 컨텍스트가 놓인 상태가 됐으므로 전역 컨텍스트와 관련된 코드의 실행을 일시 중단하고 outer 실행 컨텍스트와 관련된 코드, 즉 outer 함수의 내부 코드들을 순차적으로 실행합니다.- 마찬가지로 outer함수의 내부에 있는 inner 함수가 호출되고 실행 컨텍스트가 콜 스택 가장 위에 담기면, 아까와 마찬가지로 outer 실행 컨텍스트와 관련된 코드의 실행을 중단하고 inner 함수 내부의 코드를 순서대로 진행합니다.
- inner 함수 내부에서 변수 a에 3을 할당하고 나면 inner 함수가 종료되면서 inner 실행 컨텍스트가 제거됩니다.
- 그러면 그 아래 쌓인, outer 컨텍스트가 콜 스택 맨 위로 올라오고 a의 값을 출력하고 함수의 실행이 종료되고 outer 실행 컨텍스트가 제거됩니다.
- 마지막으로 전역 컨텍스트에서 a의 값을 출력하고 나면 더이상 실행할 코드가 없기 때문에, 전역 컨텍스트도 제거되고 콜 스택에는 아무것도 남지 않은 상태로 종료됩니다.
실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간이 곧 현재 실행할 코드에 관여하게 되는 시점이고
실행 컨텍스트가 활성화될 때, 자바스크립트 엔진은 해당 컨텍스트에 필요한 환경 정보들을 수집해서 실행 컨텍스트 객체에 저장합니다.
저장되는 환경 정보는 다음과 같습니다.
실행 콘텍스트의 환경
- VariableEnvironment
현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경정보, 선언 시점의 LexicalEnvironment의 스냅샵으로, 변경 사항은 반영되지 않습니다.
- LexicalEnvironment
처음에는 VariableEnvironment와 같지만, 변경 사항이 실시간으로 반영됩니다.
- ThisBinding
this 식별자가 바라봐야 할 대상 객체.
스냅샷이란?
마치 사진 찍듯이 특정 시점에 스토리지의 파일 시스템을 포착해 보관하는 기술
environmentRecord
호이스팅의 등장과 개념
호이스팅의 규칙
function a (x) {
console.log(x); //------------ 1
var x;
console.log(x); //------------ 2
var x = 2;
console.log(x); //------------ 3
}
a(1)
일반적으로 생각해 보았을때 1 ~ 3의 값을 예상해보자면
이렇게 생각이 들겠지만 실제로는 1, 1, 2가 나와버렸습니다.
🤔 왜 그럴까?
function a (x) {
var x; // 수집 대상 1의 변수 선언 부분
var x; // 수집 대상 2의 변수 선언 부분
var x; // 수집 대상 3의 변수 선언 부분
x = 1; // 수집 대상 1의 할당 부분
console.log(x); //------------ (1)
console.log(x); //------------ (2)
x = 2; // 수집 대상 2의 할당 부분
console.log(x); //------------ (3)
}
a(1)
그리고 이 코드는 아래의 과정을 거쳐 동작한다.
- 2번째 줄: 변수 x 선언. 메모리 저장공간을 미리 확보하고, 확보한 공간의 주솟값을 변수 x에 연결합니다.
- 3번째, 4번째 줄: 다시 변수 x를 선언하나, 이미 있으므로 무시합니다.
- 6번째 줄: x에 1을 할당. 우선 숫자 1을 별도의 메모리에 담고, x가 1의 주소값을 입력합니다.
- 7번째, 8번째 줄: 각 x출력. 모두 1이 출력됩니다.
- 9번째 줄: x에 2를 할당. 기존의 주소값을 2의 주소값으로 바꿉니다.
- 10번째 줄: x를 출력. 2가 출력됩니다.
- 함수 내부의 모든 코드가 실행됐으므로 실행 컨텍스트가 콜 스택에서 제거됩니다.
💡 물론 변수 선언할 때, var를 사용해서 이러한 문제가 생기는 것이고 let & const는 오류를 내뿜어준다,,!
function a() {
console.log(b); // (1)
var b = 'bbb'; // 수집 대상 1(변수 선언)
console.log(b); // (2)
function b() {} // 수집 대상 2(함수 선언)
console.log(b); // (3)
}
a();
다시 1 ~ 3의 값을 예상해보자면
이렇게 생각이 들겠지만 결과는 function b() {}, bbb, bbb가 나와버렸습니다.
function a() {
var b; // 수집 대상 1. 변수는 선언부만 끌어올립니다.
function b() {} // 수집 대상 2. 함수 선언은 전체를 끌어올립니다.
// var = b function b () {} // ( * )
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(); // 실행 불가능 에러!
a();
function a() {
console.log("먼저 선언 된 함수");
}
a();
function a() {
console.log("나중에 선언 된 함수");
}
a();
---결과
나중에 선언 된 함수
나중에 선언 된 함수
나중에 선언 된 함수
var a = 1;
var outer = function() {
var inner = function() {
console.log(a); // 1
var a = 3;
};
inner();
console.log(a); // - 2
}
outer();
console.log(a); // --- 3
참고 : modolee velog : 코어 자바스크립트 - 02 실행 컨텍스트
참고 : 코어 자바스크립트
스코프체인에 대한 부분이 이해가 될듯 말듯하다,,,, 내가 설명하라고 하면 못하니 다시 공부 해봐야겠다,,!