TDZ란?
- 직역하면 일시적 사각지대
- let, const, class 구문의 유효성을 관리함
- 즉, 변수를 선언 및 초기화하기 전 사이의 사각지대를 말함 (선언 및 초기화 전에 변수를 사용하게 되면 ReferenceError 에러가 발생함)
따라서 변수를 선언하기 전에 변수를 절대 사용하지 말자!
TDZ의 영향을 받는 구문
- const
- let
- class
- constructor() 내부의 super()
- 기본 함수 매개변수(Default Function Parameter)
const 변수
😃 상단에서 본 사진과 동일하다. 꼭 const 변수를 선언한 후 사용하도록 하자.
const pi = 3.14;
pi; // 3.14
let 변수
😭 let도 선언 전 줄까지 TDZ의 영향을 받는다.
count; // throws 'ReferenceError'
let count;
count = 10;
😃 따라서 let 변수 선언 후 사용하자.
let count;
count; // undefined
count = 10;
count; // 10
class 구문
😭 선언 전에 class를 사용할 수 없다.
const myCar = new Car('red'); // throws 'ReferenceError'
class Car {
constructor(color) {
this.color = color;
}
}
😃 따라서 클래스를 선언 한 후 사용하자.
class Car {
constructor(color) {
this.color = color;
}
}
const myCar = new Car('red');
myCar.color; // red
constructor() 내부의 super()
😭 부모 클래스를 상속 받았다면, 생성자 안에서 super()를 호출하기 전까지 this 바인딩은 TDZ에 있게 된다.
class MyCar extends Car {
constructor(color, power) {
this.power = power; // ⬅️
super(color);
}
}
const myCar = new MyCar('blue', '300HP'); // throws 'ReferenceError'
constructor() 안에서 super()가 호출되기 전까지 this를 사용할 수 없다.
😃 TDZ는 인스턴스를 초기화하기 위해 부모 클래스의 생성자를 호출할 것을 제안한다. 부모 클래스의 생성자를 호출하고 인스턴스가 준비되면 자식 클래스에서 this를 사용할 수 있다.
class MyCar extends Car {
constructor(color, power){
super(color);
this.power = power;
}
}
const myCar = new MyCar('blue', '300HP');
myCar.power; // '300HP'
기본 함수 매개변수(Default Function Parameter)
😭 기본 매개변수는 글로벌과 함수 스코프 사이 중간 스코프(intermediate scope)에 위치한다. 기본 매개변수도 TDZ 제한이 있다.
const a = 2;
function square(a = a) {
return a * a;
}
square(); // throws 'ReferenceError'
여기서 기본 매개변수 a는 선언 전에 `a = a` 표현식의 오른쪽에서 사용되는데 a에서 참조 에러가 발생한다. 왜냐하면 기본 매개변수는 선언 및 초기화 후에 사용되어야 하기 때문이다.
😃 이런 경우 다른 변수로 선언해서 사용한다.
const init = 2;
function square(a = init) {
return a * a;
}
square(); // throws 'ReferenceError'
TDZ의 영향을 받지 않는 구문
- var
- function
- import
이들은 현재 스코프에서 호이스팅된다.
var 변수
var 변수는 선언 전 접근하면 undefined를 얻는다.
function
함수는 선언된 위치와 상관없이 동일하게 호출된다. (함수 전체가 상단으로 올라가기 때문)
그래서 함수 선언 전 호출해도 에러가 발생하지 않고 잘 실행된다.
import 구문
import 구문이 호이스팅되므로 자바스크립트 파일 시작 부분에서 dependency 모듈을 가져오는 것이 좋다.
myFunction();
import { myFunction } from './myModule';
TDZ에서 typeof 연산자의 동작
typeof 연산자는 **변수가 현재 스코프 안에 선언되어있는지 확인할 때 유용**하다.
변수가 선언되지 않았을 때, `typeof notDefined`는 `undefined`임
typeof notDefined; // 'undefined'
TDZ에 영향을 받는 구문으로 변수를 선언했을 때, `typeof` 연산자를 사용하면 에러가 발생함
typeof variable; // throws 'ReferenceError'
let variable;
TDZ는 현재 스코프 안에서만 동작한다
TDZ는 선언문이 존재하는 스코프 범위 내에서 변수에 영향을 준다.
(ex)
function doSomething(someVal) {
// --- Function scope
typeof variable; // undefined
if (someVal) {
// --- Inner block scope
typeof variable; // throws 'ReferenceError'
let variable;
}
}
doSomething(true);
여기서 스코프 2개를 가진다.
- 함수 스코프
- let 변수가 선언된 내부 블록 스코프
⭐**TDZ는 내부 스코프에서만 존재한다!**
따라서 함수 스코프에는 영향을 미치지 않으므로 `typeof variable`은 `undefined`이고,
내부 스코프에서는 선언 전에 변수를 사용했으므로 `ReferenceError: Cannot access 'variable' before initialization` 에러가 발생한다.
Conclusion
- TDZ는 const, let, class 구문의 유효성에 영향을 미친다. 따라서 TDZ는 선언 전에 변수를 사용하는 것을 허용하지 않는다.
- 반대로 var 변수는 선언 전에도 사용할 수 있으므로 위험하다. 따라서 var 사용은 피하자.
**참고 자료**
'Front-End: Web > JavaScript' 카테고리의 다른 글
[js] Map이란? (0) | 2023.04.22 |
---|---|
[js]tilde(~)와 double tilde(~~) (0) | 2022.12.06 |
[js] 객체 복사: Object.assign vs. object spread (0) | 2022.11.28 |
[JavaScript] eval() 함수를 사용하지 마세요! eval() is evil! (0) | 2022.11.27 |
[js] lastIndexOf (+ 활용법 with indexOf) (0) | 2022.11.17 |