호이스팅(Hoisting)

클로저같은 문법들의 효용성을 이해하기위해서 자바스크립트가 가졌던 특징입니다.

var나 let, const로 정의된 변수나 함수선언문, 함수표현식이 유효범위의 최상단으로 끌어올려지는 것처럼 보여지는현상을 말합니다.

최상단으로 끌어올려지는 것처럼이라고 설명한 이유는 가상의 개념으로 설명하는 것이 좀 더 이해의 측면에 유리하기 때문입니다. 정확히 말하면 실행 컨텍스트가 활성화 되었을때 해당 영역에서 변수의 이름을 메모리에 먼저 수집하는 현상으로 인해 발생하는 현상입니다.

즉, 가상의 개념에서는 이해를 돕기위해 호이스팅을 '최상단으로 끌어올려진다'라고 말하지만 코드의 위치는 변하지 않습니다.

 

간단히 말하면,

'유효범위의 코드가 실행되기 전 메모리에 먼저 저장했던 선언문을 사용할 수 있다'는 의미입니다.

 

 

변수

let과 const로 선언한 경우에도 호이스팅 현상은 발생합니다.

하지만 액세스 할 수 없기때문에 var처럼 호이스팅을 생각하고 개발을 한다면 Reference Error가 발생합니다.

그리고 이것을 TDZ(Temporal Dead Zone)라고 합니다.

 

var

선언 후 호출하는 일반적인 방법입니다. 순서가 보장되어있기 때문에 원하는 대로 동작합니다.

var a = 'a이다';

console.log(a); // a이다

하지만, 순서가 변경되면 어떻게 될까요?

console.log(a); // undefined
var a = 'a이다';

만약 변수가 없다면 Reference Error가 발생하는데, a는 비어있는 값 undefined가 리턴됩니다.

이 부분을 '호이스팅 되었다'라고 말합니다.

호이스팅을 이해하기 쉽게 풀어 코드로 확인한다면 아래와 같습니다.

var a;

console.log(a); // undefined
a = 'a이다';

 

var와 let, const 비교

let과 const도 호이스팅은 되지만 액세스 문제로 Reference Error가 발생한다고 했는데,

어떻게 되는지 확인하기 위해 var일때의 상황을 먼저 보겠습니다.

var a = '외부 a이다';

(function() {
  console.log(a); // undefined
  var a = '내부 a이다';
}());

이렇게 선언되어있는 경우에는 새로운 function 실행컨텍스트에 의해 a가 호이스팅되며 undefined로 재 정의됩니다.

아래와 같이 풀어 해석할 수 있습니다.

var a = '외부 a이다';

(function() {
  var a;
  
  console.log(a); // undefined
  a = '내부 a이다';
}());

만약 이 부분을 let이나 const로 바꾼다면 어떻게 될까요? 

네 맞습니다 Reference Error가 발생합니다.

const a = '외부 a이다';

(function() {
  console.log(a); // ReferenceError
  const a = '내부 a이다';
}());

즉 이러한 경우들을 확인해보면

let과 const도 호이스팅이 적용되는 모습입니다.

 

하지만 가상의 개념 '끌어올려지는 현상'의 모습을 찾아볼 수 없기 때문에 let과 const에서는 발생하지 않는다고 말하는 것 같습니다.

 

 

함수 선언문과 함수 표현식의 호이스팅

함수 선언문과 함수 표현식은 함수를 새롭게 정의할 때 사용되는 방식입니다. 

함수 선언문, 함수 표현식 모두 호이스팅이 되지만 차이가 있습니다.

(이때 함수의 파라미터가 있다면 왼쪽에서 오른쪽으로 호이스팅이 됩니다.)

 

함수 선언문

function 정의부만 존재하고 별도의 할당 명령이 없습니다. 

function a(){
	console.log('a이다');
}

a(); // a이다

아래와 같은 예시라면 어떻게 될까요?

a(); // a이다

function a(){
	console.log('a이다');
}

풀어보면 아래와 같습니다.

함수 선언문일때 호이스팅 현상이 발생하고 호출 시 해당 로직을 사용할 수 있습니다.

그 이유는 함수 전체를 호이스팅하기 때문입니다.

var a = function a(){
	console.log('a이다');
};

a(); // a이다

 

함수표현식

정의한 function을 별도의 변수에 할당하는 것을 말합니다.

var는 호이스팅의 영향을 받습니다.

var a = function(){ // 익명함수 표현식
	console.log('a이다');
};

var a = function b(){ // 기명함수 표현식
	console.log('ab이다');
};

아래와 같은 예시라면 어떻게 될까요?

console.log(a); // undefined

var a = function(){
	// ....
};

풀어보면 아래와 같습니다.

일반 var와 같은 동작을 합니다.

var a;

console.log(a); // undefined

a = function(){
	// ....
};

new Function도 동일합니다. 

console.log(a); // undefined
var a = new Function('return "a"');

 

 

함수선언문, 함수표현식 비교

console.log(a);
console.log(b);

var a = function(){
	return 'a';
};

function b() {
	return 'b';
};

풀어보면 아래와 같습니다.

var a;
var b = function b() {
	return 'b';
};

console.log(a); // undefined
console.log(b); // f b() { return 'b'; }

a = function(){
	return 'a';
};

함수선언문의 호이스팅 특징때문에 실무에서 발생할 수 있는 예외사항이 생깁니다.

 

 

순서

같은 이름으로 선언했을 경우를 확인해보겠습니다

var로 선언한 변수와 함수 선언문 방식입니다.

console.log(a); // ƒ a(){ return 'a 함수'; }

var a = 'a변수';

function a(){
	return 'a 함수';
}

풀어보면 어떨까요?

예상 했던 모습으로 함수 선언문 방식이 콘솔에 찍혔습니다.

var a;

console.log(a); // ƒ a(){ return 'a 함수'; }

a = 'a변수';

function a(){
	return 'a 함수';
}

 

다시 한번 콘솔의 위치를 하단으로 이동시키고 확인해보겠습니다.

var a = 'a변수';

function a(){
	return 'a 함수';
}

console.log(a); // a변수

풀어보면 어떨까요?

이것 역시 해당 변수가 호이스팅되고 할당되는 곳의 위치는 변하지 않는 것으로 인해 a변수가 콘솔에 노출되었습니다.

변수와 함수 선언문 둘 다 호이스팅 되었지만 함수 선언문은 함수 자체가 호이스팅되기 때문에 이런 결과가 나왔습니다.

var a;
var a = function a(){
	return 'a 함수';
}

a = 'a변수';

console.log(a); // a변수

 

호이스팅 때문에 의도한 결과가 나오지 않을 수도 있으니 개발 시 해당 부분을 이해하는 것이 중요할 것 같습니다.

 


참고 및 인용

+ Recent posts