var,let,const 의 차이, 선언과할당, 호이스팅, 스코프, TDZ
var , let , const의 차이를 알기 전에
변수가 어떻게 선언되고 할당되는지 조금 더 자세히 알아야 할 필요가 있습니다.
var myNumber = 23
myNumber란 변수는 23 이다. 라고 이해하기엔 조금 부족합니다.
더 깊이 들어가면 heap, stack과 같은 개념이 등장하는 것 같지만 아직 거기까지 이해하긴 어렵습니다.
이제 조금만 업그레이드 해서
변수란, 값을 저장하기 위해 확보한 메모리 공간이라고 이해하면 더 좋을 것 같습니다.
var myNumber = 23을 작성하게 되면
변수명은 사실 23이라는 값이 아니라 메모리의 주소를 기억하고 있습니다.
그리고 변수명을 참조하려고 할 때 매핑된 메모리 주소를 찾고 거기에 저장된 23이란 값을 보여주는 거죠.
var myNumber = 23
이렇게 변수를 만들게 되면 3단계를 거치게 됩니다. [선언, 초기화, 할당]
선언이란 자바스크립트 엔진에게 myNumber라는 변수가 있다!라고 알려주는 겁니다.
그리고 23이란 값을 저장하는 것이 할당입니다.
초기화란?
선언으로 변수를 알리고 할당이 되기 전에 할당이 될 수 있는 메모리 공간을 만들고 value값을 undefined로 초기화합니다.
name에 "기원"이란 값이 할당되기 전에 참조하게 되면 undefined가 나옵니다.
즉, 자바스크립트 엔진이 name이란 변수를 알고 있고 메모리에 주소도 있지만 value는 undefined라는 뜻입니다.
선언, 초기화가 되었지만 아직 할당되지는 않은 변수입니다.
근데 이상합니다. 자바스크립트는 위에서부터 한 번에 한 줄씩 실행하는 것으로 알고 있는데, 선언과 할당을 한 줄에서 했는데
왜 선언과 초기화만 먼저 되고 할당은 나중에 되는 걸까요?
바로 선언이 자바스크립트 런타임 시점보다 빠르기 때문입니다.
변수, 함수, 클래스 전부 선언은 런타임보다 먼저 진행됩니다. 그래서 모든 선언문이 런타임보다 먼저 실행되고
변수가 어디서 선언되든지 상관없이 다른 코드보다 먼저 실행되는 특징을 호이스팅 이라고 합니다.
이번에는 스코프에 대해 이해해봅시다.
스코프는 변수의 유효범위입니다.
전역에서 선언된 변수는 전역 스코프를 가지고, 지역에서 선언된 변수는 지역 스코프를 가집니다.
전역변수는 말 그대로 어디서든 참조가 가능합니다. 지역이란 중괄호 내부를 말합니다.
자바스크립트는 모든 코드 블록(if, for, while, try/catch 등) 이 지역스코프를 만듭니다.
이러한 특성을 블록레벨 스코프라 하는데
var로 선언된 변수는 오직 함수레벨 스코프만을 인정합니다.(함수가 아니면 전부 전역스코프가 되는 겁니다.)
함수레벨 스코프와 블록레벨 스코프의 차이
var로 선언된 변수는 함수레벨의 스코프만 스코프로 인정한다는 게 왜 불편한지 설명드릴게요
왼쪽은 함수가 아닌 if문 블록 내에서 name변수를 var로 선언을 했고, 오른쪽은 함수 스코프에서 name을 선언했습니다.
함수블록이 아닌경우에는 name변수가 재선언되어 flow라는 값으로 바뀌었고
함수블록 내에서는 name변수가 재선언 되지 않고 유효 범위를 인정하여 함수 내에서 따로 쓸 수 있는 name이란 변수를 새로 만들었습니다.
만약 프로젝트가 대규모이거나 여러 사람이 협업을 하는 경우에
var를 통해 변수를 만들게 되면
어디서 전역 변수가 재선언, 재할당 되었는지 한 번에 알아차리기 힘듭니다.
그리고 실제로 해보니 사람들이 변수명을 짓는 걸 어려워하고, 비슷하게 짓는 경향이 있더라고요.
충분히 일어날 수 있는 일입니다.
이런 이유로 함수레벨 스코프를 가지는 var를 사용하는 것을 지양합니다.
var, let, const의 차이
위에서 살펴본 var를 사용하기 힘든 문제는 총 3가지입니다.
1. 변수 중복 선언이 가능하다.
2. 함수레벨 스코프를 가진다 (어디선가 나도 모르게 재선언 해서 쓰게 될지도..)
3. 선언문 이전에 참조하면 undefined를 반환한다(차리리 에러가 나면 예측 가능하고 잠재적 에러를 이 시점에 먼저 발견할 겁니다.)
이것을 let과 const로 해결할 수 있습니다.
1. 변수 중복 선언 불가
let은 중복 선언이 불가능합니다. 한번 선언된 변수는 다시 선언할 수 없습니다. 하지만 재할당은 가능합니다.
const는 중복 선언도 불가능하고 재할당도 불가능합니다. 그래서 변수가 아니라 상수라고 부릅니다.
2. 블록 레벨 스코프를 가진다.
let과 const는 모든 코드블록 (if, for, while, try/catch 등)을 모두 지역스코프로 인정합니다.
왼쪽의 var는 if문의 블록을 지역스코프로 인정하지 않고 전역변수를 재선언해버렸습니다.
오른쪽의 let은 if문의 블록을 지역스코프로 인정해서 새로운 변수를 만들어 선언하고 할당했습니다. 전역에서는 여전히 "기원"이 할당되어 있습니다.
3. 선언과 초기화가 분리되어 진행된다. 즉 선언문 이전에 참조하면 undefined가 아니라 에러를 반환한다.
var는 선언과 초기화가 동시에 이루어져서 선언문 이전에 참조하게 되면 호이스팅된 값을 불러올 때 undefined를 가져옵니다.
하지만 let과 const는 선언과 초기화가 분리되어 진행됩니다.
즉, 런타임 이전에 자바스크립트 엔진에 의해 선언이 되지만 초기화가 되지 않습니다. 즉, 아직 매핑된 메모리 주소도 없고 참조할 value도 없는 상태입니다. 그래서 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조하면 ReferenceError가 발생합니다.
초기화 전에 접근할 수 없다고 나옵니다. 이 구간을 TDZ(Temporal Dead Zone)이라고 합니다.
이것을 보고 누군가 예전에 let과 const는 호이스팅이 되지 않는다고 알려준 적이 있는데 그건 아닌 것 같습니다.
변수가 선언되기 때문에 언제나 호이스팅은 됩니다. 단지 에러 구문처럼 아직 초기화가 되지 않은 것이지요.
호이스팅이 되었다고 해도 참조할 메모리 주소가 초기화되지 않아서 에러가 발생되는 것 같습니다.
undefined보다 에러가 좋은 이유는 변수 선언 할당 단계에서 바로 에러를 만나면 예측이 가능하고 잠재적 에러에 먼저 대응할 수 있기 때문입니다.
이제 드디어 이 이미지를 이해할 수 있게 되었습니다!