이 글은 '이웅모'님의 '모던 자바스크립트 Deep Dive' 책을 통해 공부한 내용을 정리한 글입니다. 저작권 보호를 위해 책의 내용은 요약되었습니다.
자바스크립트에서 변수 선언시 var(ES5), let(ES6), const(ES6) 이렇게 세 개의 키워드를 사용한다. ES6 이전 var은 변수 선언에 유일한 키워드였다. 하지만 var에는 여러 단점이 있어 ES6 이후로는 거의 사용하지 않는다. var의 문제점은 다음과 같다.
var a = 1;
var b = 10;
var a = 100;
var b;
console.log(a); // 100
console.log(b); // 10
변수가 중복 선언 되어있으면 변수 선언과 동시에 초기값을 할당하는 문 즉, 초기화문의 유무에 따라 다르게 동작한다.
var a = 10; // 전역변수
{
var a = 100; // 중복선언
}
console.log(a); // 100
var 키워드로 선언한 변수는 함수의 코드 블록만을 지역스코프로 인정한다.
console.log(a); // undefined
a = 10;
var a;
console.log(a); // 10
호이스팅 되어 변수 선언문 이전에 참조가 가능하다. 하지만 할당문 이전에 변수를 참조하면 undefined를 반환한다.
let a = 1;
let a = 10; // SyntaxError
var 키워드로 중복선언을 했을 때 에러를 반환하지 않지만 let에서는 문법 에러가 발생한다.
let a = 1; // 전역변수
{
let a = 10; // 지역변수
console.log(a); // 10
}
console.log(a); // 1
모든 코드 블록(함수, if문, for문 등)을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.
console.log(a); // ReferenceError
let a;
console.log(a); // undefinded
a = 1;
console.log(a); // 1
호이스팅 설명에 앞서 변수의 생성단계에 대해 알아보겠다. 변수의 생성은 선언단계, 초기화단계, 할당단계 순으로 이루어진다.
var은 런타임 이전 자바스크립트 엔진에 의해 '선언단계'와 '초기화단계'가 한번에 진행되지만 let은 '선언단계'만 실행되고 '초기화단계'는 변수 선언문에 도달했을 때 실행된다는 차이점이 있다. 따라서 위 코드와 같이 'let a;' 선언문을 만나기 전까지 참조에러가 발생하는 것이다. 스코프의 시작 지점부터 변수의 초기화단계 시작 지점까지 변수를 참조할 수 없는 구간을 TDZ(일시적 사각지대)라고 부른다.
위 코드를 보면 호이스팅이 발생하지 않는 것처럼 보이지만 다음 코드를 보면 let 역시 호이스팅이 발생하는 것을 알 수 있다.
let a = 1; // 전역변수
{
console.log(a); // ReferenceError
let a = 10; // 지역변수
}
전역변수 a 값에 의해 1이 출력되길 기대했지만 호이스팅이 발생하기 때문에 위와 같이 참조에러가 나타난다.
// 브라우저 환경
var x = 1;
let y = 2;
var foo = function(a, b) {
return a+b;
}
// var 키워드로 선언한 전역변수 및 전역함수는 전역객체 window의 프로퍼티
console.log(window.x); // 1
console.log(window.y); // undefined
console.log(window.foo); // function(a,b) { return a+b; }
var 키워드로 선언된 변수는 전역 객체 window(nodejs에선 global)의 프로퍼티가 되지만 let 키워드로 선언된 변수는 그렇지않다. let과 const 키워드로 선언한 전역 변수는 전역 렉시컬 환경의 선언적 환경 레코드에 존재한다.
const a; // SyntaxError
const b = 1;
const 키워드로 선언한 변수는 반드시 선언과 초기화를 한번에 해야한다.
const a = 1;
a = 10; // TypeError
const는 재할당이 금지되어있다. 따라서 상수 역할이 용이하다.
const obj = {
name : "Lim"
}
obj.name = "Hong";
console.log(obj); // {name : "Hong"}
const는 재할당을 금지할 뿐이다. 객체는 재할당 없이도 직접 변경이 가능하기 때문에 위와 같이 const로 선언되었더라도 변경이 가능하다. 그리고 객체가 변경되었더라도 변수에 할당된 참조 값은 변경되지 않는다.
실제 코딩에는 거의 대부분 const만 사용하여 변수를 선언했던 것 같다. 즉, 재할당이 필요한 경우는 드물었다. 다만 var, let, const의 차이점을 두루뭉실하게 알고 있었는데 글을 작성하면서 차이점을 확실하게 정리할 수 있었다. 따라서 변수를 선언할 땐 다음과 같이 사용하자.