자바스크립트 호이스팅(Hoisting)
호이스팅이란?
JavaScript에서 호이스팅(hoisting)이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미합니다. var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화합니다. 반면 let과 const로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않습니다.
호이스팅을 설명할 땐 주로 "변수의 선언과 초기화를 분리한 후, 선언만 코드의 최상단으로 옮기는" 것으로 말하곤 합니다. 따라서 변수를 정의하는 코드보다 사용하는 코드가 앞서 등장할 수 있습니다. 다만 선언과 초기화를 함께 수행하는 경우, 선언 코드까지 실행해야 변수가 초기화된 상태가 됨을 주의하세요.
출처 : mdn web docs
호이스팅 대상
- 변수
- 함수
다음의 코드를 보면 호이스팅 대상에 대한 감을 잡을 수 있다.
// 예제 1
// y만 호이스팅 대상
x = 1; // x 초기화. x를 선언하지 않은 경우 선언. 그러나 명령문에 var가 없으므로 호이스팅이 발생하지 않음
console.log(x + " " + y); // '1 undefined'
// JavaScript는 선언만 호이스팅하므로, 윗줄의 y는 undefined
var y = 2; // y를 선언하고 초기화
// 예제 2
// 호이스팅은 없지만, 변수 초기화는 (아직 하지 않은 경우) 변수 선언까지 병행하므로 변수를 사용할 수 있음
a = '크랜'; // a 초기화
b = '베리'; // b 초기화
console.log(a + "" + b); // '크랜베리'
- var 없이 선언&초기화를 하는 경우 = 호이스팅 대상 아님
- var 선언&초기화를 하는 경우 =
var y
가 호이스팅 대상임,= 2
는 대상이 아님 - 오직 var 로 선언한 변수만이 호이스팅의 대상임
- 호이스팅의 대상이 되지 않았지만 예제 2의 경우 런타임에서 정상적으로 선언&초기화가 이뤄져서 인터프리터가 정상적으로 처리하여 참조값이 잘 표시됨.
1. 변수 호이스팅 var 와 let/const
다음 코드를 보면 각각 어떻게 동작하는지 알 수 있다.
####var
//사용자가 작성한 코드
console.log(a);
var a = "안녕하세요";
console.log(a);
//호이스팅이 동작한 코드
var a; // 선언&초기화 (undefined)
console.log(a); //
a = "안녕하세요";
console.log(a);
실행 결과 undefined “안녕하세요”
이처럼 값을 할당하기 위해서 작성한 var a = "안녕하세요"
에서 선언 부분인 var a
만을 호이스팅 대상으로 보고 호이스팅 한 후, undefined
로 초기화 한다. 후에 코드를 순차적으로 실행하는 런타임에서 값을 할당하여 다음 log에서는 안녕하세요
로 출력이 된 것을 볼 수 있다.
위의 경우는 var
의 경우이고 const
와 let
의 경우는 다르다.
let/const
//사용자가 작성한 코드
console.log(a);
let a = "안녕하세요"; //const도 동일
console.log(a);
//호이스팅이 동작한 코드
let a; // 선언
console.log(a); //초기화되지 않아 Runtime ERROR 발생
a = "안녕하세요";
console.log(a);
var
와 다르게 인터프리터에서 선언만 할 뿐, 호스팅을 진행하지 않아서 첫 번째 log 출력에서 undefined
를 출력하지 않고 ERRO0R가 발생한다.
var와 let/const의 호이스팅 차이
var
var
로 선언하여 Hoisting 대상이 되는 경우, 변수를 undefined
로 초기화한다.
let과 const
let
과 const
로 선언하여 Hoisting 대상이 되는 경우, 따로 변수를 초기화하지 않는다.
정리
var
로 선언하여 호이스팅 된 변수에는 자동으로 undefined
로 초기화 되기 때문에 별도로 초기화하지 않은 채로 참조를 해도 인터프리터 단에서 ERROR 를 발생시키지 않으나, let
과 const
로 선언하여 호이스팅 된 변수는 초기화 되지 않았기 때문에 이 상태에서 참조할 경우 인터프리터에서 다음과 같은 에러를 출력한다. // Uncaught SyntaxError: Identifier 'name' has already been declared
좀더 상세히는
const
와let
는 var와 다르게 선언과 초기화가 다른 단계에서 동작한다.var
는 실행 컨텍스트 Execution pahse(실행단계에서)선언과 초기화가 동시에 이뤄지는 반면,const
와let
의 경우 선언은 마찬가지로 진행되고, 초기화는 런타임에 발생한다.
2. 함수 선언문 호이스팅
함수 선언문은 변수와 마찬가지로 호이스팅의 대상이된다. 따라서 다음 코드는 정상적으로 안녕하세요
를 출력한다.
onClick();
function onClick(){
console.log('안녕하세요');
}
호이스팅 우선순위
1순위 : 변수 2순위 : 함수 선언문
//사용자가 작성한 코드
var a = "안녕";
function a() {
console.log("hi");
}
function b() {
console.log("hello");
}
var b = "하세요";
console.log(typeof a);
console.log(typeof b);
//호이스팅이 동작한 코드
//1. 변수 호이스팅
var a;
var b;
//2. 함수 선언문 호이스팅
function a() {
console.log("hi");
}
function b() {
console.log("hello");
}
a = "안녕";
b = "하세요";
console.log(typeof a); // string
console.log(typeof b); // string
여기서 a, b 값 할당이 없다면?
// 사용자가 작성한 코드
var a;
function a() {
console.log("hi");
}
function b() {
console.log("hello");
}
var b;
console.log(typeof a);
console.log(typeof b);
//호이스팅이 동작한 코드
//1. 변수 호이스팅
var a;
var b;
//2. 함수 호이스팅
function a() { //a가 함수로 초기화
console.log("hi");
}
function b() { //b가 함수로 초기화
console.log("hello");
}
console.log(typeof a);
console.log(typeof b);
3. 함수 표현식 호이스팅
함수 표현식의 호이스팅의 경우는 다음과 같다. 호이스팅의 영향을 받아 프로그램이 꼬일 여지가 많아보인다.
//사용자가 작성한 코드
a();
var a = function () {
console.log("안녕하세요");
}
//호이스팅이 동작한 코드
//변수 호이스팅
var a; // undefined
a(); // not function ERROR
a = function () {
console.log("안녕하세요");
}
정리
호이스팅의 의도를 잘 알고 잘 쓰는 것이 필요하다는 생각이 든다. 또한 var
을 지금은 사용하지 않는 것을 권장하지만 오래된 코드를 만질 일이 분명히 있고 호환성 때문에 이를 고려해야할 것인데, 동작원리를 알게 되어 var
를 쓰지 말아야하는 이유를 파악할 수 있었다. 앞으로 이 부분에 대해서 인지하는 습관을 가져야겠다.
변수 용어정리
- 선언 단계 : 변수 이름을 실행 컨텍스트에 등록해서 자바스크립트 엔진에 변수 사용을 알림
- 초기화 단계 : 값을 저장하기 위한 메모리 공간을 확보하고, undefined를 할당하여 초기화함 (이처럼 초기화 하는 이유는 연결된 확보한 메모리공간에 쓰레기 값이 남아있을 수 있기 때문이라고)
- 할당 단계 : 소스코드가 순차적으로 실행되는 시점인 런타임에 실행되며 주소 공간에 값 저장한다.