호이스팅(hoisting)

Jayden ·2023년 3월 13일
0

1. 호이스팅

console.log(a); //ReferenceError가 발생할 것 같지만 undefined가 출력

var a; // 변수 선언

자바스크립트는 변수 선언이 런타임(runtime)이 아닌 이전에 먼저 실행된다. 즉 자바스크립트 엔진은 변수 선언이 소스코드의 어디에 있든 상관없이 다른 코드 보다 먼저 실행한다. 따라서 변수 선언언이 소스코드에 어디에 위치하는지와 상관없이 어디서든지 변수를 참조할 수 있다. 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트의 고유의 특징을 변수 호이스팅(variable hoisting)이라고 한다.

변수 선언뿐 아니라 var, let, const, function, function*, class의 키워드를 사용해서 선언하는 모든 식별자(변수, 함수, 클래스 등)은 호이스팅 된다. 모든 선언문은 런타임 이전 단계에서 먼저 실행하기 때문이다.

2. 변수 호이스팅

1) var

런타임 이전에 변수 선언 + 초기화 단계가 진행된다.

// 런타임 이전에 변수 호이스팅에 의해 변수 선언
// foo의 값이 undefined로 초기화


console.log(foo); // undefined

foo = "apple";

console.log(foo); //apple

//변수 선언은 이미 자바스크립트 엔진에 의해 암묵적으로 실행
 
var foo;

2) let

console.log(foo); //Reference: foo is not defined
let foo;

var 키워드는 변수 선언 + 초기화 단계가 동시에 진행되지만, let 키워드는 변수 선언은 진행되지만, 초기화 단계는 진행되지 않는다.

따라서, 초기화 이전에 let 키워드로 선언한 값을 참조하면 위와 같이 참조 오류가 발생한다.


//런타임 이전에 선언단계 진행, 아직 변수는 초기화되지 않았다.

// 초기화 이전은 변수를 참조할 수 없다.
console.log(foo);

//변수 선언문에서 undefined로 변수 값이 초기화된다.
let foo;
console.log(foo);

// 할당문에서 변수 값이 할당 된다.
foo = "apple";
console.log(foo);

하지만, let 키워드로 선언한 변수도 역시 호이스팅이 발생한다.

let foo = "lemon"; // 전역 변수

{
 	console.log(foo); //ReferenceError : Cannot acces 'foo' before initialization 
  	let foo = "apple" // 지역 변수
    
    console.log(foo); // apple
}

만일 호이스팅이 발생되지 않는다면, foo의 참조 값은 위에 전역 변수 lemon으로 출력되겠지만, 블록 스코프 내부에서 호이스팅이 발생하여, 이후에 지역 변수 foo가 선언이 되고 console.log
이전에 값이 할당 되지 않았으므로 참조 오류가 발생한다. 선언 이후에 console.log로 foo의 값을 참조하면 변수 값 apple이 정상적으로 나오는 것을 확인할 수 있다.

스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간을 일시적 사각지대(Temporal Dead Zone)이라고 한다.

이미지 참조 : https://velog.io/@ywc8851/javascript-TDZTemporal-Dead-Zone

3) const

const로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.

const foo = "apple"

그렇지 않으면 다음과 문법 에러가 발생한다.

const foo; //SyntaxError:Missing initializer in const declarlation

const 키워드로 선언한 변수도 let 키워드로 선언한 변수와 마찬가지로 블록 레벨 스코프를 가지며, 변수 호이스팅이 발생한다.

{
	//변수 호이스팅이 발생하지 않는 것처럼 동작한다.
  	console.log(foo);
	const foo = "banana"
  	console.log(foo); //banana
}


// 블록 레벨 스코프를 갖는다.
console.log(foo); // ReferenceError : foo is not Defined

3. 함수

//함수 참조
console.dir(add); // f add(x,y)
console.dir(sub); // undefined

//함수 호출
console.log(add(2,5)); //7
console.log(sub(2,5)); //TypeError: sub is not a Function

//함수 선언문
function add(x,y){
 	return x + y;
}

// 함수 표현식
var sub = function(x,y){
 return x-y;
};

함수 선언문으로 정의한 함수와, 함수 표현식으로 정의한 함수의 생성 시점은 서로 다른데

1) 함수 선언문 : 런타임 이전에 자바스크립트 엔진에 의해 실행

함수 선언문으로 함수 정의시 런타임 전에 객체가 생성되고, 자바스크립트 엔진은 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 함수 객체를 할당한다.

또한 변수 선언문 처럼, 함수 선언문 이전에 함수를 참조할 수 있으며, 호출할 수도 있다.

2) 함수 표현식 : 변수 선언런타임 이전에 실행되어 undefined로 초기화 ,
하지만, 변수 할당문의 값은 할당문이 실행되는 시점, 즉 런타임에 평가되므로 함수 표현식의 함수 리터럴도 실행되는 시점에 평가 되어 함수 객체가 된다.

4. 클래스

클래스는 함수로 평가된다.


class Fruit{}

console.log(typeof Person);// function

클래스 선언문으로 정의한 클래스는, 함수 선언문과 같이 소스코드 평가 과정, 즉 런타임 이전에 먼저 평가되어 함수 객체를 생성한다. 이때 클래스가 평가되어 생성된 함수 객체는 생성자 함수로서 호출할 수 있는 함수는 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 더불어 생성된다. 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재하기 때문이다.

단, 클래스는 클래스 정의 이전에 참조할 수 없다.

console.log(Person);
//ReferenceError : Cannot access 'Person' before initialization
//클래스 선언문

class Person{}

클래스 선언문도 변수 선언, 함수 선언과 같이 호이스팅이 발생한다. 단, 클래스는 let, const 키워드로 선언한 변수처럼 호이스팅 된다. 따라서 클래스 선언문 이전에 일시적 사각지대 Temporal Dead Zone; TDZ에 빠지기 때문에 호이스팅이 발생하지 않는 것처럼 동작한다.

var, let, const, function, function*, class 키워드를 사용하여 선언된 모든 식별자는 호이스팅된다. 모든 선언문은 런타임 이전에 먼저 실행되기 때문이다.

(출처: 모던 자바스크립트 Deep Dive)

profile
J-SONE 프론트엔드 개발자

0개의 댓글