var, let, const
1. var 키워드의 특징 및 문제점
1-1. 변수 재선언 및 재할당 허용
var x = 1;
var y = 1;
// var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용
var x = 100; // 초기화문(값 할당)이 있는 변수 선언문
var y; // 초기화문(값 할당)이 없는 변수 선언문
console.log(x); // 100
console.log(y); // 1
var 키워드로 선언한 변수를 중복 선언하면 초기화하는 구문의 유무에 따라 동작이 다르다.
- 초기화 문이 있는 경우 : 자바스크립트 엔진이 이전 var 키워드가 없는 것처럼 동작
- 초기화 문이 없는 경우 : 해당 변수 선언문을 무시 (에러는 발생하지 않음)
❗️문제점 : 동일한 이름의 변수가 이미 선언되어 있는 것을 모르고 변수를 재선언 및 할당을 한다면 의도하지 않게 먼저 선언된 변수의 값이 바뀔 수 있는 우려가 있다.
1-2. 함수 레벨 스코프
- var 키워드로 선언한 변수는 오로지 함수의 코드 블록만 지역 스코프로 인정한다.
- 즉, 함수 외부에서 var 키워드로 선언한 변수들은 코드 블록 안에 있더라도 모두 전역 변수다.
var x = 1;
if (true) {
// x는 전역 변수다, 이미 선언된 전역 변수 x가 있으므로, x 변수는 중복 선언된다.
var x = 10;
}
console.log(x); // 10
❗️문제점 : 마찬가지로 반복문, 조건문의 블록 스코프 {} 에서 var 키워드로 선언한 변수는 전역 변수가 되기 때문에 의도치 않게 변수 값이 변경되는 부작용을 만들 수 있다.
var i = 10;
// for문에서 선언한 i 역시 전역 변수다. 이미 선언된 전역 변수 i가 있으므로 재선언된다.
for(var i = 0; i < 5; i++) {
console.log(i); // 0 1 2 3 4
}
// ??? 의도 하지 않게 i 변수의 값이 변경되었다.
console.log(i); // 5
1-3. 변수 호이스팅
- 변수 선언문이 스코프의 선두로 끌어 올려진 것처럼 동작한다.
- 즉, 변수 호이스팅에 의해 var 키워드로 선언한 변수는 선언되기 전에 참조할 수 있다. (할당하기 전에 변수를 참조하면 그 값은 언제나 undefined를 반환)
// 1. 선언 단계 : 변수 호이스팅에 의해 foo 변수가 선언된 상태
// 2. 초기화 단계 : 변수 foo는 undefined로 값이 초기화된다. (기존 쓰레기 값이 지워짐)
console.log(foo); // undefined
// 3. 할당 단계 : 변수에 값을 할당
foo = 123;
console.log(foo); // 123
var foo; // 변수 선언은 런타임 이전에 자바스크립트 엔진에 의해 평가 단계에서 암묵적으로 진행된다.
❗️문제점 : 변수 선언문 이전에 변수를 참조하는 것은 에러를 발생 시키지는 않지만, 프로그램의 흐름(위에서 아래로)에 맞지 않고, 어디서 선언했는지 찾기 힘들어 가독성을 떨어트린다.
2. let 키워드
2-1. 변수 재선언 금지
let bar = 123;
// let이나 const 키워드로 선언된 변수는 같은 스코프 내에 중복 선언을 허용하지 않음
let bar = 456; // SyntaxError: Identifier 'bar' has already been declared
let 키워드는 이름이 같은 변수를 중복 선언하면 문법 에러가 발생한다.
2-2. 블록 레벨 스코프
let 키워드로 선언한 변수는 모든 코드 블록(함수, 조건문, 반복문, try-catch문 등)을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.
let foo = 1; // 전역 변수
{
let foo = 2; // 지역 변수
let bar= 3; // 지역 변수
}
console.log(foo); // 1
console.log(bar); // ReferenceError: bar is not defined
전역에서 선언된 변수와 코드 블록 내에서 선언된 foo 변수는 다른 별개의 변수가 된다. 그리고 bar 변수도 블록 레벨 스코프를 갖는 지역 변수로 이를 전역에서 사용하게 되면 참조 에러가 발생한다.
2-3. 변수 호이스팅
console.log(foo); // ReferenceError: foo is not defined
let foo;
var 키워드와 달리 선언문 이전에 참조하면 참조 에러가 발생한다. let 키워드로 선언한 변수는 선언 단계와 초기화 단계가 분리되어 진행하기 때문에 초기화 단계는 변수 선언문에 도달했을 때 실행하게 된다.
- 일시적 사각지대(Temporal Dead Zone : TDZ) : 스코프의 시작 지점부터 초기화 단계 시작 지점까지 변수를 참조할 수 없는 구간
// 런타임 이전에 선언 단계가 실행, 변수 초기화 X
console.log(foo); // ReferenceError: bar is not defined, TDZ로 변수를 참조할 수 없음
let foo; // 변수 선언문에서 초기화 단계가 실행
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행
console.log(foo); // 1
let foo = 1; // 전역 변수
{
console.log(foo); // ReferenceError : Cannot access 'foo' before initialization
let foo = 2; // 지역 변수
}
let 키워드로 선언한 변수의 경우도 여전히 호이스팅이 발생하기 때문에 전역 변수 foo의 값을 출력하지 않고 블록 내에 foo 변수를 블록 내 최상단으로 올리기 때문에 참조 에러가 발생한다.
3. const 키워드
let 키워드의 특징과 대부분 동일하지만 재할당이 안된다는 차이점이 존재한다.
3-1. 선언과 초기화
const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.
const foo = 1;
// 선언만 하면 아래와 같이 문법 에러가 발생
const bar; // SyntaxError: Missing initializer in const declartion
let 키워드와 마찬가지로 블록 레벨 스코프를 가지며, 변수 호이스팅에서 선언과 초기화 단계가 나누어 진행되기 때문에 TDZ를 존재해서 그 전에 값을 접근하면 참조 에러가 발생한다.
3-2. 재할당 금지
const 키워드로 선언한 변수는 재할당이 금지된다.
const foo = 1;
foo = 2; // TypeError: Assignment to constant variable.
3-3. 상수
- 선언한 변수에 원시 값을 할당한 경우 변수 값을 변경할 수 없다.
- 원시 값은 변경 불가능한 값이므로 재할당 없이 값을 변경할 수 있는 방법이 없다.(이러한 특징으로 const 키워드는 상수를 표현하는데 적합하다.)
- 프로그램 전체에서 공통적으로 사용하기 위한 값으로 이용하며, 변경 사항에 대해 대처하기가 쉽기 때문에 유지 보수성 측면에서 좋다.
// 변수 이름을 "스네이크_케이스"로 선언해 상수임을 명확하게 나타낸다.
const TAX_RATE = 0.1;
let preTaxPrice = 100;
let afterTaxPrice = preTaxPrice + (preTaxPrice * TAX_RATE);
console.log(afterTaxPrice); // 110
3-4. const 키워드와 객체 ( 재할당 불가 ≠ 불변 )
- const 키워드로 선언된 변수에 객체를 할당한 경우 그 변수에 할당된 참조 주소를 변경할 수 없다. 하지만 그 주소가 참조하고 있는 주소에 직접 접근해서 값을 변경할 수는 있다.
- 즉, 새로운 값을 재할당하는 것은 불가능하지만 그 객체의 프로퍼티에 동적 생성, 삭제, 변경을 통해 객체 내부의 값이 변경하는 것은 가능하다.
4. 전역 객체와 let/const 키워드
var 키워드(전역 변수, 전역 함수, 암묵적 전역)는 전역 객체(window)의 프로퍼티가 된다.
// 브라우저 환경
// 전역 변수
var x = 1;
// 암묵적 전역
y = 2;
// 전역 함수
function foo() {}
// var 키워드로 선언한 전역 변수는 전역 객체 window의 프로퍼티
console.log(window.x); // 1
// 전역 객체 window의 프로퍼티는 전역 변수처럼 사용
console.log(x); // 1
// 암묵적 전역은 전역 객체 window의 프로퍼티
console.log(window.y); // 2
console.log(y); // 2
// 함수 선언문으로 정의한 전역 함수는 전역 객체 window의 프로퍼티
console.log(window.foo); // f foo() {}
// 전역 객체 window의 프로퍼티는 전역 변수처럼 사용
console.log(foo); // f foo() {}
반면 let/const 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니기 때문에 window객체에 접근할 수 없다. (서로 다른 개념적인 블록 단위 안에서 존재하게 되기 때문이다. )
// 브라우저 환경
let x = 1;
const y = 2;
// let, const 키워드로 선언한 전역 변수는 전역 객체 window의 프로퍼티가 아니다.
console.log(window.x); // undefined;
console.log(window.y); // undefined;
console.log(x, y); // 1 2
📋 요약
var
- 함수 레벨(단위) 스코프
- 재선언 및 재할당 허용
- 호이스팅 : 선언 + 초기화
- window 객체의 프로퍼티에 포함 O
let
- 재선언 X / 재할당 O
- 블록 레벨(단위) 스코프
- 호이스팅 : 선언 / 초기화 단계 별로 실행
- window 객체의 프로퍼티에 포함 X
const
- 재선언 및 재할당 비허용
- 블록 레벨(단위) 스코프
- 호이스팅 : 선언 / 초기화 단계 별로 실행
- window 객체의 프로퍼티에 포함 X
참조
https://poiemaweb.com/js-data-type-variable
Data type & Variable | PoiemaWeb
변수는 값의 위치(주소)를 기억하는 저장소이다. 값의 위치란 값이 위치하고 있는 메모리 상의 주소(address)를 의미한다. 즉, 변수란 값이 위치하고 있는 메모리 주소(Memory address)에 접근하기 위해
poiemaweb.com
'Language > JavaScript' 카테고리의 다른 글
코어 자바스크립트 스터디 회고 (0) | 2022.12.30 |
---|---|
Hoisting 요약 (0) | 2022.06.30 |
API와 브라우저의 객체 구조 (0) | 2021.10.22 |