아래 출처의 글 중 일부를 번역하고 제 생각을 넣은 글입니다. 의역과 오역을 자주 사용합니다.
아래의 코드중 어떤 코드가 에러를 발생시킬 것 같나요???
아래의 코드는 인스턴스를 생성합니다. 그리고 사용된 클래스를 정의합니다.
new Car('red'); // 동작할까요??
class Car {
constructor(color) {
this.color = color;
}
}
두번째는 호출을 한후에 함수의 정의를 합니다.
greet('World'); // 동작할까요?
function greet(who) {
return `Hello, ${who}!`;
}
첫번째 코드는 ReferenceError를 발생시킵니다. 두번째는 정상적으로 동작하구요
만약 답변하지 못했거나 틀렸다면 Temporarl Dead Zone(TDZ)에 대해 알 필요성이 있습니다(저도 2021년 중반에 알았던 것 같아요. 공부를 하지 않고 코딩부터 하면 이와 같은 경험을 겪을 수 있습니다.)
TDZ는 let, const, class문들의 접근성을 관리합니다. Javascript에서 어떻게 동작하는 지를 아는것은 매우 중요합니다.
(이 글에서 var대신 let, const를 사용해야 하는 필요성을 아시게 될겁니다)
1. TDZ란?
const white = '#FFFFFF';
white; // => '#FFFFFF';
위의 동작은 정상적으로 동작합니다.
아래의 동작은 white를 선언전에 참조하였기 때문에 ReferenceError가 발생합니다.
white; // throws 'ReferenceError'
const white = '#FFFFFF';
white;
const white 변수는 TDZ내에 있기 때문입니다.
TDZ에 있는 white에 접근하려 했기 때문에 Javascript는 ReferenceError: cannot access 'white' before initialization 을 반환합니다.
Temporal Dead Zone은 의미론적으로 선언전에 변수에 접근하는 것을 금지합니다.
선언전에는 어느것도 사용하지 말라는 원칙을 강요합니다.
2. TDZ에 영향을 받는 문들
2.1 const 변수
// 🔴
pi; // throws 'ReferenceError'
const pi = 3.14;
const pi = 3.14;
// 🟢
pi; // => 3.14
2.2 let 변수
let 변수 문은 선언전 까지 TDZ의 영향을 받습니다.
// 🔴
count; // throws 'ReferenceError'
let count;
count = 10;
let count;
// 🟢
count; // => undefined
count = 10;
// 🟢
count; // => 10
2.3 class
소개글에서 보았듯이 class는 선언전에 사용할 수 없습니다.
// 🔴
const mySonata = new Car('red'); // throws 'ReferenceError'
class Car {
constructor(color) {
this.color = color;
}
}
class Car {
constructor(color) {
this.color = color;
}
}
// 🟢
const mySonata = new Car('red');
mySonata.color; // => red
2.4 super() (constructor() 내부에 위치한)
부모 클래스를 상속받은 경우 construct 내부에서 super()를 호출하기 전에 this는 TDZ에 위치하게 됩니다.
class MuscleCar extends Car {
constructor(color, power) {
this.power = power;
super(color);
}
}
// 🟢
const myCar = new MuscleCar('blue', '300HP'); // 'ReferenceError'
constructor() 내부에서 super()를 호출하기 전에 this는 사용할 수 없습니다.
TDZ는 인스턴스가 활성화 될때 부모의 constructor를 호출하길 제안합니다.
class MuscleCar extends Car {
constructor(color, power) {
super(color);
this.power = power;
}
}
// 🟢
const myCar = new MuscleCar('blue', '300HP');
myCar.power // => 300HP
2.5 function 기본 인자
기본 매개 변수는 전역 및 함수 범위와 분리된 중간 범위 내에 있습니다. 기본 매개변수도 TDZ 제한을 따릅니다.
const a = 2;
function square(a = a) {
return a * a;
}
// 🔴
square(); // throws 'ReferenceError'
a = a의 우측에 쓰인 a는 선언전에 사용이 됩니다.
함수의 기본 매개변수를 a로 이름 붙였는데 이 a를 선언전에 a=a의 우측에서 사용한 것입니다(처음엔 이해가 되지 않아서 어려웠네요)
기본 매개변수는 선언과 초기화 이후에 사용하여야 합니다. 아래의 예시에 쓰인 init과 같이 특별한 변수를 사용하여야 합니다.
const init = 2;
function square(a = init) {
return a * a;
}
// 🟢
square(); // => 4
3. var, function, import 문
위에서 사용한 문들과 대조적으로 var와 function 정의는 TDZ의 영향을 받지 않습니다. 그들은 현재 범위에서 호이스팅 됩니다.
// 🟢 추천하지 않습니다.
value; // undefined
var value;
함수는 선언과 초기화 할당이 호이스팅 되어 동시에 이루어집니다.
// 🟢
greet('World'); // => 'Hello, World!'
function greet(who) {
return `Hello, ${who}!`;
}
// 🟢
greet('Earth'); // => Hello, Earth!
흥미로운 점은 import 문도 호이스팅 된다는 점입니다.
// 🟢
myFunction();
import { myFunction } from './myModule';
import 구문이 호이스팅 되기 때문에, 자바스크립트 파일 시작 부분에서 디펜던시 모듈을 가져오는 것이 좋다.
4. TDZ 에서의 typeof 동작
typeof 구문은 scope 내에서 변수가 선언됬는지 아닌지를 파악하기에 유용합니다.
예를들어 변수 notDefined는 정의되지 않았습니다. typeof 문을 적용하면 변수는 error를 던지지 않습니다.
typeof notDefined; // => 'undefined'
변수가 아직 정의되지 않았기 때문에 typeof notDefined는 undefined를 반환합니다.
하지만 TDZ 내에 있는 변수에 typeof 구문을 사용하면 다른 동작을 수행합니다.
typeof variable; // throws 'ReferenceError'
let variable;
5. 현재 범위에서 TDZ 동작
TDZ는 선언문이 있는 scope내의 변수에 영향을 줍니다.
예시를 살펴봅시다.
function doSomething(someVal) {
// 함수 스코프
typeof variable; // undefined
if (someVal) {
// 블록 스코프
typeof variable; // => throws 'ReferenceError'
let variable;
}
}
doSomething(true)
이 예시에는 2개의 스코프가 있습니다.
- 함수 스코프
- let 변수가 정의된 블록 스코프
함수 스코프에서 typeof variable는 undefined를 반환합니다. 이곳에 TDZ내의 let 변수는 영향을 주지 못합니다.
내부 스코프에서 typeof variable 문은 선언전에 변수를 사용합니다. 그래서 ReferenceError: Cannot access 'variable' before initialization 에러를 반환하게 됩니다. TDZ는 내부 스코프에만 존재합니다.
6. Conclusion
TDZ는 const, let 그리고 class 문의 사용에 영향을 끼치는 중요한 개념입니다. 선언 전에 변수의 사용을 허용하지 않습니다.
대조적으로 var 변수는 선언전에도 사용할 수 있기 때문에 var 사용은 자제하는 것이 좋습니다.
In my opinion, TDZ is one of those good things when good coding practices reach into the language specification.
개인적으로 생각하기에 const,let, class 등은 호이스팅 되어 변수에 해당하는 메모리 주솟값을 선언해두고 아직 변수에 값에 대한 주솟값은 할당하지 않은 상태인것 같습니다.
이후에 할당이 되면 원시 타입 혹은 참조 타입의 주솟값을 변수에 mapping 하게 되는 것이지요.
이후에 변수에 접근하게 되면 변수에서 가리키는 값의 주소에 접근할 수 있으므로 문제없이 사용할 수 있는게 아닐까 싶습니다.
만약 변수에만 접근하는 것이라면 undefined로 초기화 되는 var, let과 같은 특성을 가지고 있지 않다면 에러를 발생하게 될게 분명하지 않을까 싶습니다.
결국 위에서 언급한 모든 이야기와 아래에 짤막하게 메모리 관점에서 언급한 내용은 같은 내용입니다.
메모리 관점에 대한 내용은 보완이 더 필요하므로 공부해가면서 좀 더 정리할 수 있도록 하겠습니다.
출처 : https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/
'Frontend > Javascript' 카테고리의 다른 글
Javascript 프론트엔드 MV* 아키텍처 (원글 + 생각)😉 (0) | 2022.04.28 |
---|---|
Javascript Colocation (번역글) 🤔 (0) | 2022.04.26 |
Javascript undefined/undeclared/null and NaN 🧟 (0) | 2022.04.23 |
Javascript intersection observer api 👀 (0) | 2022.04.20 |
Javascript async/await, Promise 실수 포인트 🗝 (0) | 2022.04.19 |