호이스팅과 TDZ

JS (TIL & Remind)·2022년 2월 15일
1

호이스팅 이란?

인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것.
쉽게 말해, 코드를 순차적으로 실행하기 전에, 변수와 함수 선언부분을 해당 스코프의 최상단으로 끌어 올려진 것 처럼 작동하게 하는 것이다.

💡 실제로 코드가 스코프의 최상단으로 이동 되는것은 아니며, 자바스크립트의 파서(Parser)가 내부적으로 처리하는 것이다.
    console.log(foo); // undefined

    var foo = 123;
    console.log(foo); // 123

호이스팅 대상

호이스팅은 자바스크립트의 모든 변수, 함수 선언에 대해 일어난다.

단, TDZ (Temporal Dead Zone)의 영향을 받는 구문은 호이스팅이 일어나지 않는 것 처럼 보일 수 있는데, 아래의 코드에서 예시를 볼 수 있다. (TDZ에 대한 설명은 아래에서)

    console.log(foo); // Uncaught ReferenceError: foo is not defined

    const foo = 123;

TDZ (Temporal Dead Zone) 란?

TDZ(Temporal Dead Zone)는 ‘일시적인 사각지대’ 라는 뜻으로 직역되며, 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 말한다.

변수 선언의 3단계

자바스크립트에서 변수는 선언, 초기화, 할당 이라는 3가지 단계에 걸쳐 생성되는데, 이를 보고 아래의 let 변수의 라이프 사이클을 보면 TDZ에 대해 조금 더 쉽게 이해할 수 있다.

  1. 선언 단계 (Declaration phase)

    변수를 실행 컨텍스트의 변수 객체에 등록하는 단계. 이 변수 객체는 스코프가 참조하는 대상이 된다.

  2. 초기화 단계 (Initialization phase)

    실행 컨텍스트에 존재하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계. 이 단계에서 할당된 메모리에는 undefined로 초기화 된다.

  3. 할당 단계 (Assignment phase)

    사용자가 undefined로 초기화된 메모리에 다른 값을 할당하는 단계.

let 변수의 라이프 사이클

let 변수는 호이스팅 될 때 선언 단계만 실행되며, 초기화 단계는 코드가 실행될 때 일어난다.
따라서, let 변수를 초기화 하기 전에 참조하려고 하면 아직 메모리가 할당되지 않은 상태이기 때문에 참조에러가 발생한다.

console.log(foo); // Uncaught ReferenceError: foo is not defined

let foo = 123;

TDZ의 영향을 받는 구문

var와 같은 구문은 호이스팅 될 때, 선언 단계와 초기화 단계가 동시에 실행되므로 TDZ의 영향을 받지 않고 사용할 수 있다. 선언 단계와 초기화 단계가 분리되어 실행되는 즉, TDZ의 영향을 받는 구문은 다음과 같다.

let, const

console.log(foo); // Uncaught ReferenceError: foo is not defined 
console.log(bar); // Uncaught ReferenceError: bar is not defined 
let foo;
const bar = 123;

함수 표현식

var, let, const 와 동일하다. (var의 경우 undefined로 초기화 되므로 ‘is not a function’ 에러 발생)

myFunc(); // Uncaught ReferenceError: myFunc is not defined

const myFunc = () => {
  console.log('call myFunc');
}

class

const myCar = new Car('red'); // Uncaught ReferenceError: Car is not defined 

class Car {
  color = '';

constructor(color) {
  this.color = color;
}
}

constructor() 내부의 super()

부모 클래스를 상속받았다면, constructor 안에서 super() 호출 이전의 this는 TDZ에 있다.

// Does not work
class MyCar extends Car {
	constructor(color, power) {
		this.power = power;
		super(color);
	}
}

const myCar = new MyCar('red', '300hp'); // Uncaught ReferenceError: Car is not defined 

// work
class MyCar extends Car {
	constructor(color, power) {
		super(color);
		this.power = power;
	}
}

const myCar = new MyCar('blue', '300hp');

기본 함수 매개변수

기본 매개변수는 글로벌과 함수 스코프 사이의 중간 스코프에 위치한다. 따라서, TDZ 제한이 있다.

// Does not work
const a = 2;
function square(a = a) {
	return a * a;
}

square(); // Uncaught ReferenceError: Cannot access 'a' before initialization

// work
const init = 2;
function square(a = init) {
	return a * a;
}

square();

TDZ의 영향을 받지 않는 구문

다음 구문은 선언 단계와 초기화 단계가 동시에 이루어지므로 TDZ의 영향을 받지 않는다.

var

console.log(foo); // undefined
var foo = 123;

function (함수 선언문)

함수 선언문은 함수 자체가 호이스팅 된다.

myFunc(); // call myFunc
function myFunc() {
	console.log('call myFunc');
}

import

import 구문도 호이스팅 된다.

myFunc();
import { myFunc } from "myModule";
profile
노션에 더욱 깔끔하게 정리되어있습니다. (하단 좌측의 홈 모양 아이콘)

0개의 댓글