본문 바로가기

Front-End: Web/JavaScript

[js] 변수를 사용하기 전에 꼭 알아야할 "TDZ(Temporal Dead Zone)"

반응형

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개를 가진다.

  1. 함수 스코프
  2. let 변수가 선언된 내부 블록 스코프

**TDZ는 내부 스코프에서만 존재한다!**

따라서 함수 스코프에는 영향을 미치지 않으므로 `typeof variable``undefined`이고,

내부 스코프에서는 선언 전에 변수를 사용했으므로 `ReferenceError: Cannot access 'variable' before initialization` 에러가 발생한다.

Conclusion

  • TDZ는 const, let, class 구문의 유효성에 영향을 미친다. 따라서 TDZ는 선언 전에 변수를 사용하는 것을 허용하지 않는다.
  • 반대로 var 변수는 선언 전에도 사용할 수 있으므로 위험하다. 따라서 var 사용은 피하자.

 

 

 


**참고 자료**

https://ui.toast.com/weekly-pick/ko_20191014

반응형