변수 선언 키워드 (var, const, let)
ES2015 이전에는 var로 변수를 선언했다. 그러나 ES2015부터는 const와 let으로 대체했다.
스코프
가장 큰 차이점은 블록 스코프와 함수 스코프 차이이다.
if (true) {
var x = 3;
}
console.log(x); // 3
if (true) {
const y = 3;
}
console.log(y); // Uncaught ReferenceError: y is not definedvar은 함수 스코프이기 때문에 function() {} 이 스코프의 기준점이다.
하지만 const와 let은 함수 및 블록({})에도 별도의 스코프를 가진다.
호이스팅
두 번째 차이는 호이스팅(Hoisting) 방식이다. 호이스팅이란 스코프의 최상단으로 끌어올려지는 현상이다.
엄밀히 말하면 var, let, const 모두 자바스크립트 엔진에 의해 호이스팅이 발생한다. 하지만 초기화되는 방식과 TDZ(일시적 사각지대) 떄문에 동작하는 결과가 다르다.
var의 경우 호이스팅될 때 선언과 동시에 undefined로 초기화된다. 따라서 선언문보다 먼저 변수에 접근해도 에러가 발생하지 않고 undefined가 출력된다.
console.log((a); // undefined
var a = 10;let과 const는 호이스팅은 되지만, 초기화가 되지 않은 상태로 존재하게 된다.
변수가 실제 선언문을 만나기 전까지의 구간은 TDZ(Temporal Dead Zone)이라 부르며, 이때 변수에 접근하면 ReferenceError가 발생한다.
이를 통해 코드의 예측 가능성을 높여준다.
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;변수 재선언(Re-declaration)
var의 경우 같은 스코프 내에서 동일한 이름으로 여러 번 선언해도 에러가 나지 않는다. 이는 의도치 않게 값을 덮어씌워 버그를 유발하기 쉽다.
var x = 1;
var x = 2;let과 const는 같은 스코프 내에서 이미 선언된 변수 이름으로 재선언할 수 없으며 SyntaxError가 발생한다.
전역 객체 프로퍼티
var은 브라우저 환경에서 전역 변수로 선언하면 전역 객체인 window의 프로퍼티가 된다.
var globalVar = 'hi';
console.log(window.globalVar); // hilet과 const는 전역에 선언하더라도 보이지 않는 개념적인 블록 내에 존재하게 되어 전역 객체의 프로퍼티가 되지 않는다.
let globalLet = 'hello';
console.log(window.globalLet); // undefined보이지 않는 블록
위에서 설명한 보이지 않는 블록이라는 뜻에 대해 명확하게 파헤쳐보겠다.
자바스크립트 엔진이 전역 공간을 해석할 때, 전역 스코프의 환경 레코드를 두 가지로 쪼개서 관리한다. (Heap 공간에서 물리적으로 분리되어 관리)
- 객체 환경 레코드 (Object Environment Record)
여기에 var로 선언한 전역 변수나 전역 함수가 저장된다.
이 레코드는 실제 window 객체와 연결(binding)되어 있기 때문에 var로 선언하면 window로 접근이 가능하다.
- 선언적 환경 레코드 (Declarative Environment Record)
여기에는 전역 공간에 선언된 let, const, class 등이 저장된다.
이 레코드는 window 객체와 연결되어 있지 않고, 개념적으로 전역 공간 내부에 let, const를 위한 최상위 독립 공간(보이지 않는 블록)이 하나 더 존재한다.
전역 호출 탐색 과정
위 내용을 기반으로 전역 공간에서 x를 호출하면 어떤 순서로 변수를 탐색하는지 다뤄보겠다.
-
전역 공간에서
x를 호출하면, 자바스크립트 엔진은 스택에서 힙에 있는 두 공간을 찾는다. -
먼저 힙에 있는 선언적 환경 레코드 공간에서 먼저 탐색을 한다. (있으면 가져오고 TDZ를 체크)
-
없으면, 힙에 있는 객체 환경 레코드가 가리키는
window객체 내부를 탐색한다. (둘 다 없다면ReferenceError)
const
const는 상수라고 생각하면 된다. 따라서 값을 한 번 선언하면 다른 값으로 변환이 안된다.
const a = 3;
a = 5; // 에러
const c;
c = xxxx; // 에러그러나 객체를 넣었을 때는 내부 값 변경이 가능하다.
const b = {name : 'jihun'};
b.name = 'yang';