JS에서는 변수 선언 방식에 따라 변수 생성 위치가 다르다.
1. var를 이용하여 변수를 선언하면 선언한 위치와 상관없이 함수의 맨 위에 있는 것처럼 처리된다.
- 만약 변수를 선언한 위치가 어떠한 함수 내에도 포함되어 있지 않다면 전역 스코프의 최상단에서 선언한 것처럼 처리된다.
- 이것을 호이스팅(hoisting)이라고 한다.
- 즉,
호이스팅
이란 var를 이용하여 변수를 선언한 경우, 선언한 위치와 상관없이 함수의 맨 위에 있는 것처럼 처리되는 현상이다.
- 단,
호이스팅
이 일어나더라도 변수의 선언만 함수의 맨 위로 끌어올려질 뿐, 값의 할당은 끌어올려지지 않는다. 호이스팅이 일어난 변수에 값의 할당이 이뤄지기 전에 접근하면, 변수는 undefined 값을 가진다.
- var로 선언된 변수는 함수 스코프이다. (함수 내에서만 접근 가능하다.)
2. 블록 스코프
- 블록 스코프는 함수 내부와 블록 내부에서 만들어집니다.
- let 선언: let으로 선언된 변수는 블록의 맨 위로 호이스팅되지 않으며, 스코프는 현재의 코드 블록으로 제한됩니다.
- const 선언의 경우 let 선언과 동일하나, const를 사용하여 선언된 바인딩은 상수로 간주되어, 추후에 새로운 값을 할당할 수 없습니다. 다만, const로 선언된 변수에 할당된 값이 객체인 경우, 객체의 프로퍼티를 변경할 수 있습니다. 즉, const 선언은 바인딩을 변경하지 못하도록 하는 것이지, 바인딩 된 값의 변경을 막는 것은 아닙니다. const는 바인딩의 수정을 방지하는 것이지, 바인딩된 값의 수정을 막는 것이 아닙니다.
- 모든 const 선언은 선언과 동시에 할당이 이루어져야 합니다. (선언만 한 경우 SyntaxError 가 발생합니다)
3. let 선언과 const 선언의 경우 같은 스코프 안에 존재하는 식별자를 재정의하는 경우, 에러가 발생합니다.
그에 반해 var 선언의 경우 식별자를 재정의하여도 에러가 발생하지 않습니다. var 선언의 경우 선언은 모두 함수의 맨 위로 끌어올려지므로, 식별자를 재정의하여도 선언은 한 번 밖에 하지 않는 것과 동일하기 때문입니다.
- 하지만, 기존 변수와 같은 이름의 식별자를 새로운 블록 스코프 내부에 정의한다면 에러가 발생하지 않습니다. 새로운 블록 스코프 내부에서는 외부 스코프의 변수에 접근할 방법은 없으므로, 이러한 변수명은 추천되지 않습니다. (중첩된 스코프 내에 기존 변수와 동일한 이름의 변수가 존재하는 것을 shadowing 이라고 합니다)
4. TDZ(Temporal Dead Zone)
- TDZ는 ECMAScript 스펙에 명시되지는 않았지만, let과 const 바인딩이 왜 선언 이전에 접근할 수 없는지를 설명하기 위해 종종 사용된다.
- JS엔진은 블록을 실행하기 전 블록을 조사하고 그 블록에서 변수 선언을 발견하면, 그 선언을 (var의 경우) 함수 최상단 / 전역 스코프로 호이스팅하거나 (let, const의 경우) TDZ내에 배치합니다.
- TDZ안의 변수에 접근하려 하면, 런타임 에러가 발생합니다. 변수 선언이 실행된 후에만 TDZ에서 변수가 제거되며, 안전하게 사용할 수 있습니다.
반면, 선언된 변수가 있는 블록 바깥에서 변수에 접근하는 경우, 변수는 TDZ에 존재하지 않으므로 JS엔진은 런타임 에러를 발생시키지 않으며 undefined를 반환합니다. 즉, TDZ는 블록 바인딩의 독특한 특징 중 하나입니다.
Q. Reference Error가 발생하는데? Uncaught ReferenceError: b is not defined
Follow-up
- 변수 vs 바인딩 개념정리
블로그 글 잘 읽었습니다!!
작성해주신 글에서는 let과 const가 호이스팅이 일어나지 않는다고 하였는데 제가 알기로는 let과 const도 호이스팅이 일어난다고 알고 있습니다~
한 번 찾아보셔도 좋을 것 같아욯ㅎ