[JS] var, let, const의 차이점 + 변수호이스팅 , 스코프
var, let, const의 차이점은
크게 변수 선언 방식, 스코프(scope), 호이스팅(hoisting), 그리고 재할당 가능 여부에서 나타난다.
1. var (과거 방식)
- 함수 스코프(function scope)를 가진다.
- 변수 선언 전에 사용할 수 있지만 undefined가 할당된다. (호이스팅)
- 동일한 변수명을 중복 선언할 수 있다.
- 재할당 가능.
console.log(a); // undefined (호이스팅 때문에 오류가 나지 않음)
var a = 10;
if (true) {
var b = 20; // 블록 내부에서 선언해도 바깥에서 접근 가능 (함수 스코프)
}
console.log(b); // 20 (의도치 않은 값 변경 가능)
➡ var는 함수 스코프이기 때문에 블록 {} 내부에서 선언해도 바깥에서 접근할 수 있다.
➡ var는 선언 전에 접근해도 undefined가 할당되므로 예상치 못한 동작이 발생할 수 있다.
▼ 함수 스코프(Function Scope)란?
함수 스코프(Function Scope)는 변수가 함수 내에서 선언되면, 그 함수 내부에서만 접근할 수 있고, 함수 밖에서는 접근할 수 없는 스코프(범위) 를 의미 한다.
즉, 함수가 끝나면 변수도 사라지는 것
var 키워드로 선언한 변수는 함수 스코프를 가진다. 즉, 함수 내부에서 선언된 변수는 함수 바깥에서 접근할 수 없다.
function example() {
var a = 10; // 함수 스코프 변수
console.log(a); // 10
}
example();
console.log(a); // ReferenceError: a is not defined
✅ a는 example() 함수 내부에서 선언되었기 때문에 함수가 끝나면 사라진다.
✅ 함수 바깥에서 a에 접근하려 하면 오류가 발생한다.
var는 함수 스코프를 가지지만 블록 스코프는 없다 ( 주의 )
if (true) {
var b = 20; // if 블록 안에서 선언
}
console.log(b); // 20 (블록을 벗어나도 접근 가능)
✅ var는 함수 스코프이기 때문에 함수가 아닌 블록 {} 내부에서 선언해도 바깥에서 접근할 수 있다.
✅ if, for, while 같은 블록 내부에서 선언해도 밖에서 접근이 가능하다.
✅ 이런 문제 때문에 let과 const가 나왔다.
만약 파일 내에 함수가 존재하지 않는다면 var는 어떤 함수 스코프를 가지고 있는 걸까?
✅ 함수가 없으면 var x는 전역 스코프(Global Scope) 에 선언됨
✅ var로 선언된 전역 변수는 window(브라우저 환경) 또는 globalThis(모든 환경에서 사용 가능) 에 저장됨
▼ 호이스팅 (Hoisting) 이란?
var는 호이스팅, let, const 는 TDZ가 존재하는 호이스팅호이스팅(Hoisting)은 JavaScript 엔진이 변수와 함수 선언을 코드 실행 전에 메모리에 미리 저장하는 동작을 의미한다.
즉, 코드가 작성된 순서와는 다르게, 변수와 함수의 선언이 코드의 최상단으로 끌어올려지는 것처럼 동작하는데
실제로 코드의 위치가 변경되는 것이 아니라, JavaScript가 내부적으로 선언을 미리 처리하는 것이다.
(1) var의 호이스팅
var로 선언된 변수는 호이스팅 시 선언만 되고 초기값은 undefined로 설정된다.
console.log(a); // undefined (오류 X, but 값이 없음)
var a = 10;
console.log(a); // 10
실제 실행 과정
var a; // 선언이 먼저 메모리에 저장됨 (호이스팅)
console.log(a); // undefined
a = 10; // 이후에 값이 할당됨
console.log(a); // 10
✅ var는 호이스팅되지만, 초기화(a = 10)는 호이스팅되지 않음
✅ 그래서 a 선언 전에 console.log(a)를 실행하면 오류가 아니라 undefined가 출력됨
(2) let과 const의 호이스팅 (TDZ 존재)
let과 const도 호이스팅되지만, 선언 전에 접근하면 오류(ReferenceError)가 발생한다.
이것을 Temporal Dead Zone(TDZ, 일시적 사각지대) 라고 해.
console.log(b); // ReferenceError (TDZ로 인해 접근 불가)
let b = 20;
console.log(b); // 20
실제 실행 과정
// 1. 메모리 할당 (호이스팅 발생, but TDZ에 위치한 상태)
let b; // 선언은 되었지만 초기화되지 않음 (TDZ)
// 2. 아직 초기화되지 않았기 때문에 ReferenceError 발생
console.log(b); // ❌ ReferenceError
// 3. 변수 초기화 및 값 할당
b = 20;
// 4. 이제는 정상적으로 접근 가능
console.log(b); // ✅ 20
✅ let과 const도 호이스팅돼서 메모리 할당이 일어나지만 undefined로 초기화 되진 않아서 접근할 수 없다.
✅ 초기화는 TDZ가 끝난 후(let b= 20; 실행된 후) 이루어진다.
✅ var와 다르게 undefined가 아니라 아예 오류(ReferenceError) 발생
✅ var, let 둘 다 선언 후 값을 할당하지 않으면 undefined가 출력됨.
✅ 하지만 var는 선언과 동시에 undefined로 초기화되고, let은 선언 이후 초기화되므로 TDZ가 존재함.
✔ var는 호이스팅되지만 undefined가 할당되므로 예기치 않은 동작이 발생할 수 있음 → let과 const를 사용하자.
✔ let과 const도 호이스팅되지만, TDZ로 인해 선언 전에 접근하면 오류가 발생
2. let
- 블록 스코프(block scope)를 가진다.
- 변수 선언 전에 접근하면 오류(ReferenceError) 발생. (호이스팅은 되지만 TDZ(Temporal Dead Zone) 존재)
- 동일한 변수명을 중복 선언할 수 없다.
- 재할당 가능.
console.log(x); // ReferenceError (TDZ 때문에 선언 전 접근 불가)
let x = 10;
if (true) {
let y = 20;
}
console.log(y); // ReferenceError (블록 스코프 적용)
➡ let은 블록 {} 내부에서 선언된 변수는 바깥에서 접근할 수 없다.
➡ 변수 선언 전에 접근하면 오류가 발생하므로 예기치 않은 동작을 방지할 수 있다.
이때 블록 {} if, for, while 뿐만 아니라 함수의 {} 도 포함한다. 따라서 함수도 똑같이 내부에서만 접근 가능하다. let, const
3. const
- 블록 스코프(block scope)를 가진다.
- 변수 선언 전에 접근하면 오류(ReferenceError) 발생. (TDZ 존재)
- 동일한 변수명을 중복 선언할 수 없다.
- 재할당이 불가능하다. (하지만 객체의 속성은 변경 가능)
const PI = 3.14;
PI = 3.1415; // TypeError (재할당 불가)
const obj = { name: "Alice" };
obj.name = "Bob"; // 가능 (객체 속성 변경은 가능)
console.log(obj); // { name: "Bob" }
- 배열이나 객체와 같은 참조 타입은 const로 선언해도 그 내부의 내용은 변경할 수 있다.
- 하지만 배열 자체를 재할당하거나 새로운 배열로 바꾸는 것은 불가능함
const arr = [1, 2, 3];
// 배열 자체의 재할당은 불가능
arr = [4, 5, 6]; // 오류 발생!
// 배열의 요소는 변경 가능
arr[0] = 10; // arr: [10, 2, 3]
arr.push(4); // arr: [10, 2, 3, 4]
console.log(arr); // [10, 2, 3, 4]
const obj = { name: "Alice" };
// 객체의 프로퍼티 수정 가능
obj.name = "Bob"; // obj: { name: "Bob" }
// 객체 자체의 재할당은 불가능
obj = { name: "Charlie" }; // 오류 발생!
차이점 정리
| var | let | const | |
| 스코프 | 함수 스코프 | 블록 스코프 | 블록 스코프 |
| 호이스팅 | 선언만 됨 (초기값 undefined) | 선언만 됨 (TDZ 존재, 접근x ) | 선언만 됨 (TDZ 존재 , 접근x ) |
| 중복 선언 | 가능 | 불가능 | 불가능 |
| 재할당 가능 여부 | 가능 | 가능 | 불가능 |
var, let, const를 각각 언제 사용해야 할까?
1. 항상 변수는 선언하고 사용해야 한다.
2. 값을 변경하지 않아야 하는 경우 : const 을 기본으로
3. 유형을 변경하면 안 되는 경우(배열 및 객체) : const
4. const를 사용할 수 없는 경우 -> 변경될 가능성이 있는 경우 : let
5. 오래된 브라우저를 꼭 지원해야 하는 경우 : var