자바스크립트 변수 선언은 var로만 가능했지만 ES6(ES2015)부터 let과 const가 추가되었다.
예전의 var가 let(변수), const(상수)로 분리되었다 생각할 수 있으나, 내부 사정은 상당히 다르다.
1. 중복선언 가능 여부
2. 재할당 가능 여부
3. 변수 스코프 유효범위
4. 변수 호이스팅 방식
5. 전역객체 프로퍼티 여부
1. var - 중복 선언이 가능하다
// 첫번째 변수 선언 + 초기화
var a = 10;
console.log(a); // 10
// 두번째 변수 선언 + 초기화
var a = 20;
console.log(a); // 20
// 세번째 변수 선언(초기화X)
var a;
console.log(a); // 20
var로 선언한 변수는 중복해서 선언(+초기화)이 가능
이 경우, 마지막에 할당된 값이 변수에 저장된다.
단, 초기화 없이 선언만 한 경우에는 선언문 자체가 무시된다.(에러발생X)
기존에 선언해 둔 변수의 존재를 까먹고, 값을 재할당하게 되는 등 실수 발생 가능성 有
2. const, let - 중복 선언 불가능
// let 중복 선언
let a = 10;
let a = 20; // SyntaxError: Identifier 'a' has already been declared
// const 중복 선언
const b = 10;
const b = 20; // SyntaxError: Identifier 'b' has already been declared
let, const로 선언한 변수는 중복 선언이 불가능하다.
코드에서 보는 것처럼 이미 선언한 변수를 다시 선언할 경우, 에러가 발생한다.
var에 비해서 코드의 안정성을 높여줄 수 있는 방식
1. var, let - 값의 재할당이 '가능'한 변수
var a = 10;
a = 20;
console.log(a); // 20
let b = 111;
b = 222;
console.log(b); // 222
var와 let은 변수를 선언하는 키워드.
변수 선언 및 초기화 이후에 반복해서 다른 값을 재할당 할 수 있다.
2. const - 값의 재할당이 '불가능'한 상수
const c = 111;
c = 222; // TypeError: Assignment to constant variable.
const는 상수를 선언하는 키워드다.
처음에 선언 및 초기화하고 나면 다른 값을 재할당 할 수 없다.
*const는 처음 선언할 때 반드시 초기화(값 할당)를 해줘야한다.
const a = 10;
const b; // SyntaxError: Missing initializer in const declaration
스코프란? 유효한 참조 범위
예를 들어, 함수 내부에서 선언된 변수는 함수 내부에서만 참조가 가능하다.
이 경우 변수의 스코프는 함수 내부로 한정된다.
자바스크립트는 var로 선언한 변수의 스코프와 let, const로 선언한 변수의 스코프가 다르다.
var는 함수 내부에 선언된 변수만 지역변수로 한정하며, 나머지는 모두 전역변수로 간주한다.
function hello() {
var a = 10;
console.log(a);
}
hello(); // 10
console.log(a); // ReferenceError: a is not defined
hello 함수 내부에서 선언된 a 변수는 함수 내부에서만 참조가 가능하며, 외부에서 참조 시 에러가 발생한다.
하지만, 함수를 제외한 영역에서 var로 선언한 변수는 '전역변수'로 취급된다.
if(true) {
var a = 10;
console.log(a); // 10
}
console.log(a); // 10
자바스크립트에서는 if, for, while, try/catch문 등의 코드 블럭{...}내부에서 var로 선언된 변수를 전역 변수로 간주한다.
그래서 블럭 외부에서도 어디에서나 참조할 수 있다.
그러나 let과 const는 다르다.
let, const는 함수 내부는 물론, if문이나 for문 등의 코드블럭{...}에서 선언된 변수도 지역변수로 취급한다.
if(true) {
let a = 10;
console.log(a); // 10
}
console.log(a); // ReferenceError: a is not defined
if문의 블럭 내부에서 let으로 선언된 변수는 외부에서 참조되지 않음을 알 수 있다.
당연히 함수 내부에서 선언된 변수도 외부에서 참조할 수 없다.
function hello() {
let a = 10;
console.log(a); // 10
}
console.log(a); // ReferenceError: a is not defined
정리하자면, var는 함수 내부에 선언된 변수만 지역변수로 인정하는 함수 레벨 스코프
let, const는 블록 내부에서 선언된 변수까지도 지역변수로 인정하는 블록 레벨 스코프
*참고로, 블록은 if문이나 for문 등에서 중괄호{}로 둘러싸인 코드 영역
자바스크립트는 코드를 실행하기 전, 일종의 '코드 평가 과정'을 거치는데, 이 때 '변수 선언문'을 미리 실행해두기 때문에 뒤에서 선언된 변수도 앞의 코드에서 참조할 수 있게 된다.
이를 변수 호이스팅이라고 한다.
var와 let/const는 호이스팅 과정도 다르다.
console.log(a); // undefined
var a = 10;
console.log(a); // 10
뒤에서 선언된 변수 a가 앞에서 참조되었음에도 에러를 발생시키지 않는다.
코드 실행전에 자바스크립트 엔진이 미리 1)변수를 선언하고, 2)undefined로 초기화 해 두었기 때문이다.
이게 바로 var로 선언된 변수의 호이스팅이다.
console.log(a); // ReferenceError: a is not defined
let a = 10;
뒤에서 선언된 변수를 앞에서 참조하려 하니 에러가 발생한다.
마치 호이스팅이 발생하지 않는 것처럼 보인다.
이런 현상이 발생하는 이유는 let, const의 호이스팅 과정이 var와 다르게 진행되기 때문이다.
let/ const로 변수를 선언하는 경우,
코드 실행 전에는 1)변수 선언만 해두며 2)초기화는 코드 실행 과정에서 변수 선언문을 만났을 때 수행한다.
그래서 호이스팅이 발생하기는 하지만, 값을 참조할 수 없어서 호이스팅이 발생하지 않는 것처럼 보이는 것이다.
변수 선언과 초기화 사이에 일시적으로 변수 값을 참조할 수 없는 구간을 TDZ(Temporal Dead Zone)이라고 한다.
그렇다면, 호이스팅이 발생하는 걸 어떻게 확인할 수 있을까?
아래의 두 코드를 비교해보면 알 수 있다.
let a = 10; // 전역 변수 a 선언
if(true) {
console.log(a); // ReferenceError: a is not defined
let a = 20; // 지역 변수 a 선언
}
이 코드는 if문 블럭 내부에서 지역변수 a를 다시 선언했다.
이 경우, 지역변수 a 앞에서 console.log()로 참조 시 에러가 발생한다.(전역변수a있어도)
왜냐하면, 지역변수 a가 호이스팅되면서 TDZ구간이 만들어졌기 때문이다.
즉, let으로 선언된 변수도 호이스팅이 발생함을 알 수 있다.
(참고로 지역변수가 전역변수보다 우선 순위를 가짐)
var a = 10;
console.log(window.a); // 10
console.log(a); // 10
브라우저 환경(크롬 콘솔 등)에서 위 코드 실행 시, var로 선언한 변수 a는 브라우저 전역 객체인 window의 프로퍼티로 할당된다.
let a = 10;
console.log(window.a); // undefined
console.log(a); // 10;
브라우저 환경에서 위 코드 실행 시, let이나 const로 선언한 변수 a는 브라우저 전역객체인 window의 프로퍼티로 할당되지 않았음을 알 수 있다.
(window.a = undefined)
*참고 - 구글의 자바스크립트 스타일 가이드에 다음과 같은 문장이 있다.
Declare all local variables with either const or let.
Use const by default, unless a variable needs to be reassigned.
The var keyword must not be used.