[Javascript] 호이스팅 (Hoisting)

곽태민·2023년 4월 25일
0

TIL

목록 보기
17/65

호이스팅 (Hoisting)


호이스팅 (Hoisting) 이란 함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 번위의 최상단에 선언하는 것을 말한다. 자바스크립트 함수는 실행되기 전에 함수 안에 필요한 변수값들을 모두 모아 유효 범위 최상단에 선언한다.

자바스크립트 Parser가 함수 실행 전 해당 함수를 한번 훑고, 함수 안에 존재하는 변수/함수 선언에 대한 정보를 기억하고 있다가 실행 시킨다. 이때, 유효 범위는 함수 블록 {} 안에서 유효한다.

즉, 함수 내에 아래쪽에 존재하는 내용 중 필요한 값들을 끌어올리는 것이다. 실제로 코드가 끌어 올려지지 않고, 자바스크립트 Parser 내부적으로 끌어올려서 처리한다. 이 과정에서는 실제 메모리 변화는 없다.

호이스팅 대상

var 변수 선언과 함수선언문에서만 호이스팅이 일어난다. var 변수/함수 선언만 위로 끌어올려지고 할당은 끌어 올려지지 않는다. letconst 변수 선언과 함수표현식에서는 호이스팅이 발생하지 않는다.

예시1 (var 변수 VS let/const 변수)

console.log('hello');

var name = 'kim';
let name2 - 'lee';
// 위 코드와 동일한 코드로 호이스팅 결과를 표시한 코드

var name;	// 호이스팅 선언
console.log('hello');

name = 'kim';	// 할당
let name2 = 'lee';	// 호이스팅 발생 X

예시2 (함수선언문 VS 함수표현식)

foo();
foo2();

function foo () {	// 함수선언문
	console.log('hello');
}

var foo2 = function() {	// 함수표현식
	console.log('hello2');
}
// 위 코드와 동일한 코드로 호이스팅 결과를 표시한 코드

var foo2; // 호이스팅 함수표현식의 변수값 선언

function foo() {	// 호이스팅 함수선언문
	console.log('hello');
}

foo();
foo2(); // Error

foo2 = function() {
	console.log('hello2');
}

호이스팅은 함수선언문과 함수표현식에서 서로 다르게 동작하기 때문에 주의해야한다. 변수에 할당된 함수표현식은 끌어 올려지지 않기 때문에 이때는 변수의 스코프 규칙을 그대로 따른다.

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


함수선언문에서 호이스팅

함수선언문은 코드를 구현한 위치와 관계없이 자바스크립트 특징인 호이스팅에 따라서 브라우저가 자바스크립트를 해석할 때 맨 위로 끌어 올려진다.

function printName(firstname) {	// 함수선언문
	var result = inner();		// 선언 및 할당
  	console.log(typeof inner);	// function
  
  	function inner() {			// 함수선언문
    	return 'inner value';
    }
}

printName();
// 위 코드와 동일한 코드로 호이스팅 결과를 표시한 코드

function printName(firstName) {
	var result;					// 호이스팅 var 변수 선언
  
	function inner() {			// 호이스팅 함수선언문
    	return 'inner value';
    }
  
  	result = inner();			// 할당
  	console.log(typeof inner);	// function
  	console.log('name is ' + result); // name is inner value
}

printName();

해당 예제에서는 함수선언문이 아래에 있어도 printName 함수 내에서 innerfuntion으로 인식하기 때문에 오류가 발생하지 않는다.

함수표현식에서 호이스팅

함수표현식은 함수선언문과 다르게 선언과 호출 순서에 따라서 정상적으로 함수가 실행되지 않을 수 있다. 함수표현식에서는 선언과 할당의 분리가 발생한다.

함수표현식의 선언이 호출보다 위에 있는 경우 = 정상 출력

function printName(firstname) {	// 함수선언문
	var inner = function() {	// 함수표현식
    	return 'inner value';
    }
    
    var result = inner();		// 함수 호출
  	console.log('name is ' + result);
}

printName();	// name is inner value
// 위 코드와 동일한 코드로 호이스팅 결과를 표시한 코드

function printName(firstname) {
	var inner;				// 호이스팅 함수표현식 변수값 선언
  	var result;				// 호이스팅 var 변수값 선언
  
  	inner = function() {	// 함수표현식 할당
    	return 'inner value';
    }
  
  	result = inner();		// 함수 호출
  	console.log('name is ' + result);
}

printName();	// name is inner value

함수표현식의 선언이 호출보다 아래에 있을 경우 - TYPE ERROR

function printName(firstName) {	// 함수 선언문
	console.log(inner);			// undefined - 선언은 됐는데 값이 할당되어 있지 않음.
  	var result = inner();		// ERROR
  	console.log('name is ' + result);
  
  	var inner = function() {	// 함수표현식
    	return 'inner value';
    }
}

printName();	// TypeError : inner is not a function
// 위 코드와 동일한 코드로 호이스팅 결과를 표시한 코드

function printName(firstname) { 
     var inner; 			// 호이스팅 함수표현식의 변수값 "선언"

     console.log(inner); 	// undefined
     var result = inner(); 	// ERROR
     console.log("name is " + result);

     inner = function() { 
         return "inner value";
     }
 }
 printName(); // TypeError: inner is not a function

printName에서 "inner is not defined"라고 오류가 안나오고 "inner is not a function"이라고 나오는 이유는

printName이 실행되는 순간 호이스팅에 의해서 inner는 "undefined"로 지정되기 때문에 inner가 "undefined"라는 것은 아직은 함수로 인식이 되지 않고 있다는 것을 의미한다.

함수표현식 선언이 호출보다 아래에 있는 경우 (const/let 변수 할당) = ReferenceError

function printName(firstName) {	// 함수선언문
	console.log(inner)			// Error
  	let result = inner();
  	console.log('name is ' + result);
  
  	let inner = function() {
    	return 'inner value';
    }
}

printName();	// ReferenceError : inner is not defined

letconst 경우 호이스팅이 일어나지 않기 때무에 위 예시와 같이 그대로 해석하면된다. console.log(inner);에서 inner에 대한 선언이 되어있지 않아서 이때는 inner is not defined 오류가 발생한다.

함수표현식보다 함수선언문을 더 자주 사용하지만, 어느 코딩컨벤션에서는 함수표현식을 권장하기도 한다. 즉, 어떤 컨벤션을 갖던지 한가만 정해서 사용하는 것이 좋다.

호이스팅 우선순위


같은 이름의 var 변수 선언과 함수 선언에서 호이스팅

변수 선언이 함수 선언보다 위로 끌어올려진다.

var name = 'hello';

function myName() {
	console.log('kim');
}

function yourName() {
	console.log('lee');
}

var yourName = 'Bye';

console.log(typeof myName);
console.log(typeof yourName);
// 위 코드와 동일한 코드로 호이스팅 결과를 표시한 코드

// 호이스팅 변수값 선언
var myName;
var yourNme;

// 호이스팅 함수 선언문
function myName() {
	console.log('kim');
}
function yourName() {
	console.log('lee');
}

// 변수값 할당
myName = 'hello';
yourName = 'Bye';

console.log(typeof myName);		// string
console.log(typeof yourName);	// string

값이 할당되어 있지 않은 변수와 값이 할당되어 있는 변수의 호이스팅

var myName = 'kim';	// 값 할당
var yourName;		// 값 할당 X

function myName() {		// 같은 이름의 함수 선언
	console.log('myName Function');
}

function yourName() {	// 같은 이름의 함수 선언
	console.log('yourName Function');
}

console.log(typeof myName);		// string
console.log(typeof yourName);	// string

값이 할당되어 있지 않은 변수인 경우, 함수선언문이 변수를 덮어쓰고, 값이 할당되어 있는 변수일 경우, 변수가 함수선언문을 덮어쓴다.

호이스팅 사용 시 주의


코드의 가독성과 유지보수를 위해 호이스팅이 일어나지 않도록 한다. 호이스팅을 제대로 모르더라도 함수와 변수를 가급적 코드 상단부에 선언하면, 호이스팅으로 인한 스코프 꼬임 현상은 방지할 수 있고, 되도록이면 letconst를 사용한다.

var를 쓰면 혼란스럽고 쓸모없는 코드가 생길 수 있다. 그럼 var와 호이스팅을 왜 이해를 해야하냐면 ES6를 어디서든 쓸 수 있으려면 시간이 더 필요하고 ES5로 트랜스컴파일을 해야한다.

따라서 아직은 var가 어떻게 동작하는지 이해해야한다.

profile
Node.js 백엔드 개발자입니다!

0개의 댓글