1. 실행 컨텍스트란?
실행 컨텍스트
실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고, "콜스택"에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서 보장한다.
실행 컨텍스트를 구성하는 방법은 "함수 실행"이다.
실행 컨텍스트 동작
- 처음 자바스크립트 코드를 실행하는 순간 "전역 컨텍스트"가 콜스택에 담음.
- outer 함수를 호출하면서 outer에 대한 환경 정보를 수집해서 "outer 실행 컨텍스트"를 생성하여 콜스택에 담음.
- inner 함수를 호출하면서 inner에 대한 환경 정보를 수집해서 "inner 실행 컨텍스트"를 생성하여 콜스택에 담음.
- inner 함수의 실행이 종료되면서 "inner 실행 컨텍스트"가 콜스택에서 제거.
- outer 함수의 실행이 종료되면서 "outer 실행 컨텍스트"가 콜스택에서 제거.
- 전역 공간에 더는 실행할 코드가 남아 있지 않으면 "전역 컨텍스트"가 콜스택에서 제거.
(새로운 실행 컨텍스트가 콜스택에 쌓이면, 이전에 실행되었던 코드를 일시중단하고 새로운 실행 컨텍스트가 제거되면 일시중단 했던 코드가 다시 실행됨.)
실행 컨텍스트에 담긴 정보
실행 컨텍스트에는 VariableEnvironment, LexicalEnviroment, ThisBinding이 담겨 있다.
2. VariableEnvironment
VariableEnvironment에 담기는 내용은 LexicalEnvironment와 같지만 최초 실행 시의 스냅샷을 유지한다는 점이 다르다.
즉 실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 먼저 담은 다음에 이를 그대로 복사해서 LexicalEnvironment를 만들고, 이후에는 LexicalEnvironment를 주로 활용하게 된다.
VariableEnvironment와 LexicalEnvironment의 내부에는 environmentRecord와 outerEnviromentReference로 구성되어 있다.
3. LexicalEnvironment
LexicalEnvironment의 내부에는 environmentRecord와 outerEnviromentReference로 구성되어 있다.
3-1. environmentRecord와 호이스팅
environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다. 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집한다. 그래서 코드가 실행되기 전임에도 불구하고 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명들을 모두 알고 있게 된다.
여기서 바로 '호이스팅' 개념이 등장하게 된다. 자바스크립트 엔진이 실제로 끌어올리지는 않지만 변수 정보를 수집하는 과정을 더욱 이해하기 쉽게 편의상 끌어올린 것으로 간주하자는 것이다.
호이스팅 규칙
변수는 선언부와 할당부를 나누어 선언부만 끌어올리는 반면, 함수 선언은 함수 전체를 끌어올린다.
// 원본 코드
function a() {
console.log(b); // (1)
var b = 'bbb'; // 수집 대상 1 (변수 선언)
console.log(b); // (2)
function b() {} // 수집 대상 2 (함수 선언)
console.log(b); // (3)
}
// 호이스팅 마친 상태
function a() {
var b; // 수집 대상 1. 변수는 선언부만 끌어올립니다.
function b() {} // 수집 대상 2. 함수 선언은 전체를 끌어올립니다.
console.log(b); // (1)
b = 'bbb'; // 변수의 할당부는 원래 자리에 납겨둡니다.
console.log(b); // (2)
console.log(b); // (3)
}
// 호이스팅 마친 상태 (함수 선언문을 함수 표현식으로 바꾼 코드)
function a() {
var b;
var b = function b() {}; // 바뀐 부분
console.log(b); // (1) b 함수
b = 'bbb';
console.log(b); // (2) 'bbb'
console.log(b); // (3) 'bbb'
}
함수 선언문과 함수 표현식
- 함수 선언문: function 정의부만 존재하고 별도의 할당 명령이 없는 것.
- 함수 표현식: 정의한 function을 별도의 변수에 할당하는 것.
- (익명) 함수 표현식: 함수명을 정의한 함수 표현식
- 기명 함수 표현식: 함수명을 정의하지 않은 함수 표현식
function a () {...} // 함수 선언식, 함수명 a가 곧 변수명
a(); // 실행
var b = function() {...) // (익명) 함수 표현식, 변수명 b가 곧 함수명
b(); // 실행
var c = function d () {...} // 기명 함수 표현식, 변수명은 c, 함수명은 d
c(); // 실행
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;
};
// 호이스팅 마친 상태
var sum = function sum(a, b) {
return a + b;
};
var multiply;
console.log(sum(1, 2)); // 3
console.log(multiply(3, 4)); // 에러 발생
multiply = function (a, b) {
return a * b;
};
3-2. 스코프, 스코프 체인, outerEnvironmentReference
스코프란? 식별자에 대한 유효범위이다.
스코프 체인이란? 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해 나가는 것이다.
스코프 체인을 가능케 하는 것이 바로 LexicalEnvironment의 두 번째 수집 자료인 outerEnvironmentReference이다.
스코프 체인
outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조한다.
예를 들어 A함수 내부에 B함수를 선언하고 다시 B함수 내부에 C함수를 선언한 경우, 함수 C의 outerEnvironmentReference는 함수 B의 LexicalEnvironment를 참조한다. 함수 B의 LexicalEnvironment에 있는 outerEnvironmentReference는 다시 함수 B가 선언되던 때(A)의 LexicalEnvironment를 참조하게 된다.
이처럼 outerEnvironmentReference는 연결리스트 형태를 띠며, 선언 시점의 LexicalEnvironment를 계속 찾아 올라가면 마지막엔 전역 컨텍스트의 LexicalEnvironment가 있을 것이다.
가장 가까운 요소부터 차례대로 접근하기 때문에, 동일한 식별자를 선언한 경우에는 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능하게 된다.
스코프 체인 확인
크롬 브라우저에서는 상위 스코프 정보들을 개발자 도구의 콘솔을 통해 확인할 수 있다.
outer 스코프에 b와 inner가 있는 것을 확인할 수 있다.
디버거를 사용하면 좀 더 정확한 정보를 확인할 수 있다.
4. this
실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장된다. 실행 컨텍스트 활성화 당시에 this가 지정되지 않은 경우 this에는 전역 객체가 저장된다. 그 밖에는 함수를 호출하는 방법에 따라 this에 저장되는 대상이 달라진다.
5. 정리
실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다. 실행 컨텍스트 객체는 활성화되는 시점에 VariableEnvironment, LexicalEnviroment, ThisBinding의 세 가지 정보를 수집한다.
LexicalEnviroment는 매개변수명, 변수의 식별자, 선언한 함수의 함수명 등을 수집하는 environmentRecord와 바로 직전의 컨텍스트의 LexicalEnvironment 정보를 참조하는 outerEnvironmentReference로 구성되어 있다.
스코프는 변수의 유효범위이며, 코드 상에서 어떤 변수에 접근하려고 하면 현재 LexicalEnvironment를 탐색해서 그 값을 반환하고, 발견하지 못할 경우 다시 outerEnvironmentReference에 담긴 LexicalEnvironment를 탐색하는 과정을 거친다. 만약 전역 컨텍스트의 LexicalEnvironment까지 탐색해도 해당 변수를 찾지 못하면 undefined를 반환한다.
'💜 프론트엔드 > JavaScript' 카테고리의 다른 글
[코어자바스크립트] 04. 콜백 함수 (0) | 2025.01.15 |
---|---|
[코어자바스크립트] 03. this (0) | 2025.01.15 |
[코어자바스크립트] 01. 데이터 타입 (1) | 2024.12.23 |
[JavaScript] 이벤트 버블링, 캡쳐링, 위임 & HTTP 메소드 (0) | 2024.04.07 |
[JavaScript] var, let, const 중복 선언 허용, 스코프, 호이스팅 (0) | 2024.03.31 |
FE 개발자가 되고 싶은 짱잼이
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!