원문 : https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/
(출처 : https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/)
간단한 질문을 하나 하겠다. 아래 코드 스니펫에서 에러가 발생할까?
첫 번째 코드는 인스턴스를 생성한 다음 클래스를 선언한다.
new Car('red'); // Does it work?
class Car {
constructor(color) {
this.color = color;
}
}
두 번째 코드는 함수를 실행한 다음 함수를 선언한다.
greet('World'); // Does it work?
function greet(who) {
return `Hello, ${who}!`;
}
정답은 다음과 같다. 첫 번째 코드에서는 ReferenceError
에러가 발생하고, 두 번째 코드는 잘 동작한다.
만약 예상한 답과 다르거나 어떤 일이 일어나는지 잘 모르고 추측만 했다면, 당신은 Temporal Dead Zone(TDZ)을 알 필요가 있다.
TDZ은 let
, const
, class
구문의 유효성을 관리한다. 자바스크립트에서 변수가 동작하는 방식은 중요하다.
const
변수 선언부터 시작해보자. 변수를 선언하고 초기화하면 변수에 접근할 수 있다. 예상한대로 동작한다.
const white = '#FFFFFF';
white; // => '#FFFFFF'
이번에는 선언 전에 white
변수에 접근해보도록 하겠다.
white; // throws `ReferenceError`
const white = '#FFFFFF';
white
const white = '#FFFFFF'
구문 전 줄까지, white
변수는 TDZ에 있다.
TDZ에 있는 white
변수에 접근하게 되면 , ReferenceError: Cannot access 'white' before initialization
자바스크립트 에러가 발생한다.
(출처 : https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/)
TDZ 시맨틱은 선언 전에 변수에 접근하는 것을 금지한다. TDZ는 징계를 내린다: 변수 선언 전에 어떤 것도 사용하지 않는다.
TDZ의 영향을 받는 구문들을 살펴보자.
이전에 보았듯이, const
변수는 선언 및 초기화 전 줄까지 TDZ에 있다.
// Does not work!
pi; // throws `ReferenceError`
const pi = 3.14;
const
변수는 선언한 후에 사용해야 한다.
const pi = 3.14;
// Works!
pi; // => 3.14
let
도 선언 전 줄까지 TDZ의 영향을 받는다.
// Does not work!
count; // throws `ReferenceError`
let count;
count = 10;
다시, let
변수도 선언 이후에 사용해야 한다.
let count;
// Works!
count; // => undefined
count = 10;
// Works!
count; // => 10
머리말 부분에서 보았듯이, 선언 전에는 class
를 사용할 수 없다.
// Does not work!
const myNissan = new Car('red'); // throws `ReferenceError`
class Car {
constructor(color) {
this.color = color;
}
}
이 예제가 동작하려면, 클래스를 선언한 후에 사용하도록 수정한다.
class Car {
constructor(color) {
this.color = color;
}
}
// Works!
const myNissan = new Car('red');
myNissan.color; // => 'red'
부모 클래스를 상속받았다면, 생성자 안에서 super()
를 호출하기 전까지 this
바인딩은 TDZ에 있다.
class MuscleCar extends Car {
constructor(color, power) {
this.power = power;
super(color);
}
}
// Does not work!
const myCar = new MuscleCar(‘blue’, ‘300HP’); // `ReferenceError`
이 코드를 보면 constructor()
안에서 super()
가 호출되기 전까지 this
를 사용할 수 없다.
TDZ는 인스턴스를 초기화하기 위해 부모 클래스의 생성자를 호출할 것을 제안한다. 부모 클래스의 생성자를 호출하고 인스턴스가 준비되면 자식 클래스에서 this 값을 변경할 수 있다.
class MuscleCar extends Car {
constructor(color, power) {
super(color);
this.power = power;
}
}
// Works!
const myCar = new MuscleCar('blue', '300HP');
myCar.power; // => '300HP'
기본 매개변수는 글로벌과 함수 스코프 사이의 중간 스코프(intermidiate scope)에 위치한다. 기본 매개변수 또한 TDZ 제한이 있다.
const a = 2;
function square(a = a) {
return a * a;
}
// Does not work!
square(); // throws `ReferenceError`
기본 매개변수 a
는 선언 전에 a = a
표현식의 오른쪽에서 사용되었다. a
에서 참조 에러가 발생한다.
기본 매개변수는 선언 및 초기화 다음에 사용되어야 한다. 이 경우 init
과 같은 다른 변수로 선언하여 사용한다.
const init = 2;
function square(a = init) {
return a * a;
}
// Works!
square(); // => 4
위에서 설명한 것들과 반대로 var
, function
선언은 TDZ에 영향을 받지 않는다. 이것들은 현재 스코프에서 호이스팅 된다.
var
변수는 선언하기 전에 접근하면, undefined
를 얻게 된다.
// Works, but don't do this!
value; // => undefined
var value;
그러나 함수는 선언된 위치와 상관없이 동일하게 호출된다.
// Works!
greet('World'); // => 'Hello, World!'
function greet(who) {
return `Hello, ${who}!`;
}
// Works!
greet('Earth'); // => 'Hello, Earth!'
당신은 함수 구현보다 호출에 더 관심이 있기 때문에 종종 함수 선언 전에 호출하게 된다. 함수 선언 전에 호출해도 에러가 발생하지 않는 이유는 호이스팅 때문이다.
흥미로운 점으로 import
모듈 역시 호이스팅 된다.
// Works!
myFunction();
import { myFunction } from './myModule';
import
구문이 호이스팅 되기 때문에, 자바스크립트 파일 시작 부분에서 디펜던시 모듈을 가져오는 것이 좋다.
typeof
연산자는 변수가 현재 스코프 안에 선언되었는지 확인할 때 유용하다.
예를 들어서, notDefined
변수는 선언되지 않았다. 이 변수에 typeof
연산자를 적용하면 에러가 발생한다.
typeof notDefined; // => 'undefined'
변수가 선언되지 않았기 때문에, typeof notDefined
는 undefined
로 평가한다.
그러나 TDZ의 변수에서 typeof
연산자를 사용하면 다르게 동작한다. 다음과 같은 경우에 에러가 발생한다.
typeof variable; // throws `ReferenceError`
let variable;
TDZ은 선언문이 존재하는 스코프 범위 안에서 변수에 영향을 준다.
(출처 : https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/)
예제를 보자.
function doSomething(someVal) {
// Function scope
typeof variable; // => undefined
if (someVal) {
// Inner block scope
typeof variable; // throws `ReferenceError`
let variable;
}
}
doSomething(true);
이 코드는 2개의 스코프를 가진다.
let
변수가 선언된 내부 블록 스코프함수 스코프에서 typeof variable
는 undefined
로 평가된다. 여기서는 let variable
구문의 TDZ에 영향을 주지 않는다.
typeof variable
구문의 내부 스코프에서는 선언 전에 변수를 사용하면 ReferenceError: Cannot access 'variable' before initialization
에러가 발생한다. TDZ는 내부 스코프에서만 존재한다.
TDZ는 const
, let
, class
구문의 유효성에 영향을 미치는 중요한 개념이다. TDZ는 선언 전에 변수를 사용하는 것을 허용하지 않는다.
반대로 var
변수는 선언 전에도 사용할 수 있기 때문에 var
사용은 피하는 것이 좋다.
TDZ는 언어 스펙에 맞도록 좋은 코딩 습관을 만들어주기 때문에 좋다고 생각한다.