자바스크립트에서 변수가 메모리에 할당되는 과정은 선언, 초기화, 할당의 세 단계로 이루어집니다.
먼저 변수 선언 단계에서는 자바스크립트 엔진이 실행 전에 코드 전체를 스캔하며, 변수 이름을 메모리에 등록해 둡니다.
이 과정은 호이스팅(Hoisting)이라고 합니다.그 다음 초기화 단계에서
var키워드는undefined로 초기화되고,
let과const는 초기화되지 않은 상태로 일시적 사각지대(Temporal Dead Zone)에 머무릅니다.마지막으로 코드 실행 중 실제 값이 할당되면, 해당 메모리 공간에 그 값이 저장됩니다.
즉, 변수 선언 시점부터 메모리 공간이 확보되지만, 값이 실제로 들어가는 시점은 할당 단계입니다.
또한 원시값은 스택(Stack)에, 객체나 배열 같은 참조형 값은 힙(Heap)에 저장되며, 변수는 힙의 주소를 참조하게 됩니다.
자바스크립트에서 변수는 선언 → 초기화 → 할당의 흐름으로 다뤄진다.
여기에 호이스팅(선언이 위로 끌어올려지는 효과), TDZ(일시적 사각지대), 스코프와 환경 레코드, 가비지 컬렉션이 얽히면서 실제 동작이 결정된다.
자바스크립트 엔진은 실행 전에 코드를 한 번 훑으며 환경(스코프)과 바인딩(변수 이름 → 메모리 슬롯)을 준비한다.
이를 흔히 호이스팅으로 설명한다.
varundefined로 초기화undefinedlet / constconst는 선언과 동시에 초기화(값 필요)가 규칙임var/let/const)을 그대로 따름코드가 실제로 실행되면서 각 선언문을 만날 때 초기화가 일어난다.
let x; → 이 지점에서 비로소 x가 초기화(접근 가능)됨const y = 10; → 이 줄에서 선언과 초기화, 할당이 동시에 이뤄져야 함var z; → 이미 undefined로 초기화된 상태임console.log(a); // var: undefined
var a = 1;
console.log(b); // ❌ let: ReferenceError (TDZ)
let b = 1;
console.log(c); // ❌ const: ReferenceError (TDZ)
const c = 1;
자바스크립트는 렉시컬 스코프를 갖고, 각 스코프는 내부적으로 Lexical Environment로 표현된다.
{} 블록, let/const 바인딩 저장var는 함수 스코프(또는 스크립트 전역) 환경 레코드에 기록let/const는 블록 스코프 환경 레코드에 기록let/const는 선언문 실행 시점에 초기화가 이루어지므로, 그 전 접근은 ReferenceErrorvar: 전역 객체(브라우저의 window)에 동일 이름의 프로퍼티를 생성let/const: 전역 객체 프로퍼티와 분리된 모듈/전역 레코드에 저장🔎 스크립트 전역, 모듈 전역
브라우저에서 자바스크립트를 넣는 방식은 크게 두 가지
- 스크립트 전역:
<script src="..."></script>(기본)- 모듈 전역:
<script type="module" src=".."></script>두 방식은 전역 바인딩 방식과 엄격 모드, top-level
this등이 다름🔎 스크립트 전역에서의 바인딩
✅
var또는 함수 선언식
- 전역에서
var x = 1;를 쓰면 전역 객체(window)에 동일 이름의 프로퍼티가 생김- 즉,
window.x === x가true- 이 프로퍼티는 삭제 불가(non-configurable)라서
delete window.x가false를 반환함- 함수 선언식(
function f() {})도 전역에서는 전역 객체 프로퍼티로 바인딩됨<script> var x = 1; function f() {} console.log(window.x === x); // true console.log('x' in window); // true console.log(delete window.x); // false (삭제 불가) </script>✅
let/const
- 전역에서
let y = 1; const z = 2;는 전역 객체(window)에 프로퍼티를 만들지 않음- 즉,
window.y와window.z는undefined- 그래도 전역 스코프에서 식별자로는 접근됨 (전역 환경 레코드에 존재)
delete y같은 시도는 문법/동작상 허용되지 않음<script> let y = 1; const z = 2; console.log(window.y); // undefined console.log(typeof y); // "number" console.log('y' in window); // false </script>✅ 정리
var/함수 선언식 ⇒ window 프로퍼티 생성(전역 객체 오염 가능, 삭제 불가)let/const⇒ window와 분리된 전역 바인딩(클린)🔎 모듈 전역에서의 바인딩
✅ 공통 규칙
- 모듈은 항상 Strict Mode
- 모듈의 전역은 모듈 전역 스코프로, 전역 객체와 분리됨
- 따라서
var이라도 모듈 최상위에서 window 프로퍼티를 만들지 않음<script type="module"> var a = 1; let b = 2; const c = 3; console.log(window.a); // undefined console.log(window.b); // undefined console.log(window.c); // undefined </script>✅ top-level
this
- 스크립트 전역(비엄격)에서는
this === window가true- 모듈 전역에서는
this === undefined가true<script> console.log(this === window); // true (비 엄격 스크립트) </script> <script type="module"> console.log(this); // undefined (모듈) </script>🔎 왜 이 차이가 중요할까?
1. 전역 오염 위험
- 스크립트 전역에서
var/함수 선언은 window에 그대로 노출되어 네임스페이스 충돌 위험이 큼- 모듈(또는 전역
let/const)은 window와 분리되어 전역 오염을 줄임2. 삭제/재선언/속성 속성(attribute)
- 스크립트 전역의
var/함수 선언은 삭제 불가(non-configurable) 프로퍼티라delete가 실패한다.- 반면 모듈 전역은 window 프로퍼티를 만들지 않으므로 이런 이슈가 없다.
3. 엄격 모드 & 안전성
- 모듈은 기본적으로 strict mode라서, 암묵적 전역 생성 등 위험한 패턴이 차단된다.
🔎 표로 정리하기
구분 스크립트 전역( <script>)모듈 전역( <script type="module">)var x = 1window.x생성됨 (삭제 불가)window.x생성 안 됨function f() {}window.f생성됨 (삭제 불가)window.f생성 안 됨let/const전역 바인딩은 생기지만 window 프로퍼티 아님 동일 (window와 분리) top-level this비 엄격이면 this === windowthis === undefined엄격 모드 기본 아님( 'use strict'필요)기본 엄격 모드