실행 컨텍스트(Execution Context)
- 자바스크립트 코드가 실행되는 환경
- 코드가 실행될 때 필요한 모든 정보를 담고 있는 추상적인 컨테이너
실행 컨텍스트의 구성 요소
JavaScript 를 실행하려면 컴파일과 실행이라는 두 단계가 필요하다.
JS 코드가 컴파일되면 실행 컨텍스트가 생성되고, 실행 컨텍스트가 준비되면 실행 단계가 시작된다.
컴파일 단계 (Compile) | 실행 단계 (Excution) |
변수와 함수 선언 실행 컨텍스트 생성 변수 환경 초기화 호이스팅 |
변수 할당 함수 실행 코드를 줄 단위로 실행 변수 환경 업데이트 |
⚠︎ 주의할 점
- 같은 이름의 함수와 변수가 있을 경우 함수가 우선된다.
- 조건문 내부의 변수 선언도 컴파일 단계에서 처리된다.
실행컨텍스트의 주요 4가지 구성요소
- Variable environment
- Lexical environment
- Outer
- this
변수 환경(Variable environment)과 렉시컬 환경(Lexical environment)
실행 컨텍스트 생성 시 변수는 두 환경에 동시에 생성된다.
초기 단계에 Variable Environment(VE)에 변수 정보를 저장한 다음, 이를 그대로 복사해 Lexical Environment(LE) 생성한다.
VE 는 초기 상태를 유지하고(스냅샷 역할), LE 는 코드 실행에 따라 변수 상태를 지속적으로 업데이트 한다.
Variable Environment
- 초기 변수 상태를 저장
- 호이스팅 관련 정보를 제공
- 이후 변경사항은 Lexical Environment 에서 변수 상태를 관리한다.
- 대부분 Lexical environment 와 동일하지만, let 과 const 는 약간 다르게 처리된다.
Lexical Environment
- 현재 실행 중인 변수 및 함수 식별자를 관리(변수 참조 및 수정 등)
- 스코프와 스코프 체인을 관리
- Environment Record : 실제 변수와 함수 정보 저장
- Outer Environment Reference : 상위 스코프 연결
❗️ 차이점
초기에는 VE와 LE 가 동일하지만 코드 진행에 따라 LE 만 변화하고, VE는 초기 상태를 유지한다.
실행컨텍스트의 동작
예시 1)
var apple = 10;
console.log(apple);
① 컴파일
- 코드가 실행되면 첫번째로 컴파일이 되어 실행 컨텍스트가 생성된다
- apple 변수가 VE와 LE 에 저장된다.
- 선언된 변수 apple 은 undefined 로 초기화
② 실행
- apple 변수에 숫자 10이 할당된다. (실제 메모리 값 변경)
- LE 에서 최신 변수 상태를 추적한다.
- console.log() 가 LE 에서 변수값 10을 읽어 출력한다.
종료
- 전체 실행 컨텍스트 제거
예시 2)
console.log(apple);
var apple = 10;
① 컴파일
- 첫 번째 줄은 변수 선언과 관련이 없으므로 건너뛴다.
- 두번째 줄의 apple 변수가 VE 와 LE 에 저장된다.
- apple 변수는 undefined 로 초기화 된다.
② 실행
- 첫 번째 줄에서 콘솔은 LE 에서 apple 변수를 찾아 undefined 값을 출력한다.
- 두 번째 줄에서 apple 변수에 10이 할당된다.
종료
✅ 주요 포인트
- 변수 할당은 실제로 컴파일, 실행 두 단계로 나뉜다.
- 컴파일 단계에서 Variable Environment 는 초기 변수 상태를 저장한다.
- 실행 단계에서는 값 할당과 나머지 코드를 실행한다.
변수 호이스팅
console.log(x); // undefined
var x = 5;
console.log(y); // ReferenceError
let y = 10;
console.log(z); // ReferenceError
const z = 15;
var 호이스팅
- 선언과 초기화가 컴파일 단계에서 같이 이루어진다.
- 초기값은 undefined
- 선언 전 접근 시 undefined 을 반환한다.
let / const 호이스팅
- 선언만 컴파일 단계에서 이루어지고, 초기화는 실행 단계에서 이루어진다.
- 선언과 초기화 사이의 구간을 Temporal Dead Zone(TDZ)이라고 하는데
- TDZ 내에서 변수에 접근하면 ReferenceError가 발생한다.
var 와 let / const 의 주요 차이점
var | let / const |
함수 스코프, 호이스팅 시 undefined로 초기화 | 블록 스코프, 호이스팅 시 초기화되지 않음 (TDZ 존재) |
✅ 주의사항
- 가능한 let과 const를 사용하여 예측 가능한 스코프 동작을 유지하자
함수와 호이스팅
함수를 선언하는 주요 방법과 호이스팅의 차이에 대해 알아보자.
함수 선언식 (Function Declaration)
function greet(name) {
return `Hello, ${name}!`;
}
- function 정의부만 존재하고 별도의 할당 명령이 없는 것
- 반드시 함수명이 정의되어있어야 함
- 코드의 어느 위치에서든 호출 가능
- 함수 선언식은 전체 함수가 호이스팅된다.
함수 표현식 (Function Expression)
// (익명) 함수 표현식 - 변수명 greet 가 곧 함수명
var greet = function(name) {
return `Hello, ${name}!`;
};
// 기명 함수 표현식 - 변수명은 greet, 함수명은 sayHello
var greet = function sayHello(name) {
return `Hello, ${name}!`;
};
- 정의한 function 을 별도의 변수에 할당하는 형태
- 함수명이 없어도 됨
- (익명) 함수 표현식 - 간결하고 일반적으로 많이 사용됨
- 기명 함수 표현식 - 재귀나 복잡한 로직에서 자기 참조가 필요할 때 유용
- 함수 표현식은 변수 선언만 호이스팅되고, 함수 할당은 실행 시점에 이루어지므로 할당 이후에만 사용 가능
함수의 호이스팅
greet("hayoung"); // 정상 작동 (함수 선언식)
bye("hayoung"); // TypeError: bye is not a function (함수 표현식)
// 함수 표현식
var bye = function(name) {
return `bye, ${name}!`;
};
// 함수 선언식
function greet(name) {
return `Hello, ${name}!`;
}
① 컴파일
- 함수 표현식인 bye 함수는 값이 undefined 로 할당되어 두 환경에 저장된다.
- 함수 선언식인 greet 함수는 greet = funciton () {...} 함수 전체가 두 환경에 저장된다.
② 실행
- 함수 선언식 greet은 전체 함수가 호이스팅되어 정상적으로 호출된다.
- 함수 표현식 bye는 undefined로 초기화되어 호출 시 오류가 발생한다.
종료
❗️주의사항
- 코드의 가독성과 예측 가능성을 위해 함수 선언은 코드 상단에 배치하자
- 가능한 한 함수 선언식보다는 함수 표현식을 사용하자
❓함수 표현식을 권장하는 이유
- 스코프 관리: 함수 표현식은 스코프 제한 가능, 모듈화에 유리
- 호이스팅 문제 예방 : 예측가능한 코드 실행
- 가독성&유지보수 : 변수에 할당 시 코드 흐름과 함수 생성/사용 시점이 명확
- 유연한 활용 : 클로저, 고차 함수, 콜백 등에 적합
- ES6 화살표 함수와의 호환성 : 간결한 문법, this의 lexical 바인딩 제공
- 디버깅 용이성: 함수 생명주기 명확한 제어
✅ 함수와 변수의 메모리 저장 방식(스택과 힙 개념과 연결되어 정리)
1. 함수저장
- 함수 자체가 VE, LE 에 바로 저장되는게 아니라 함수 자체는 Heap 메모리에 저장되고, VE와 LE 에는 함수의 식별자(이름)와 Heap 메모리 참조 주소가 저장된다.
2. 변수저장
- 원시타입의 작은 크기 데이터(숫자, 문자열, 불리언 등)는 값 자체가 VE, LE 에 직접 저장될 수 있고 참조 타입(객체, 배열 등)의 데이터는 Heap 메모리에 저장되고 VE, LE 에는 Heap 메모리 참조 주소가 저장된다.
스코프, 스코프 체인, outerEnvironmentReference
스코프(Scope)
식별자(변수와 함수) 가 접근 가능한 범위
ES5 까지의 자바스크립트에서는 전역공간과 함수에 의해서만 스코프가 생성되었다.
ES6 부터는 블록에 의해서도 스코프가 생성 될 수 있게 되어 { } 로 감싸진 블록 내에서도 독립적인 스코프가 형성된다.
블록 레벨 스코프는 let 과 const 로 선언된 변수에만 적용되고 var 로 선언된 변수는 여전히 함수 레벨 스코프를 따른다.
+ ES6 에서 도입된 class 선언과 strict mode 에서의 함수 선언도 블록 레벨 스코프를 따른다.
- 전역 스코프 : 코드의 가장 바깥쪽 범위로, 어디서든 접근 가능
- 함수 스코프 : 함수 내부에서 선언된 변수는 해당 함수 내에서만 접근 가능
- 블록 스코프 : let 과 const 로 선언된 변수는 중괄호 { } 내에서만 접근 가능
스코프 체인(Scope Chain)
식별자(변수와 함수)를 안에서부터 바깥으로 차례로 검색해 나가는 것
(현재 스코프) → (바깥쪽 스코프)
* 전역 스코프까지 반복
outerEnvironmentReference
스코프 체인을 구현하는 내부 메커니즘
각 실행 컨텍스트는 outerEnvironmentReference 를 가지며, 이는 현재 호출된 함수가 선언될 당시의 LE 를 참조한다.
* 선언될 당시 : 콜 스택 상에서 실행 컨텍스트가 활성화 된 상태
이를 통해 내부 함수는 외부 함수의 변수에 접근할 수 있다. (외부에서 내부는 접근 X)
* 변수 은닉화
내부 스코프에서 외부 스코프와 동일한 이름의 변수를 선언하면, 내부 변수가 외부 변수를 '가리게' 되는데 이때 내부에서 외부 변수에 직접 접근할 수 없다.
이때 전역 변수에 접근하기 위해서는 window 객체(브라우저) or global 객체(Node.js) 를 통해 접근이 가능하다.
'자바스크립트' 카테고리의 다른 글
콜백함수 (0) | 2024.12.14 |
---|---|
this (0) | 2024.12.13 |
얕은복사와 깊은복사, structuredClone() (0) | 2024.12.02 |
스택(Stack)과 힙(Heap) (0) | 2024.12.02 |
데이터 타입 (0) | 2024.11.26 |