Javascript를 배우다 보면 꼭 한번은 알게 되는 개념이다.
호이스팅(Hoisting)이란 단어 자체가 올리다 끌어올리다 란 의미를 가지고 있는데 정말 말 그대로다.
개발을 배울 때 듣게되는 언어들이 해석하게 되면 의미 그대로 사용되는 경우가 많은데 정말 개발자 마인드를 그대로 보여주는 게 아닌가 싶다
일단 Javascript 코드는 스코프 별로, 평가(컴파일)된 후 실행되며, 평가 시 호이스팅 된다.
여기서 평가는 컴파일러 언어가 아니라 인터프리터로서의 컴파일(선언문 실행 + 호이스팅)이다
로 함수 코드는 사용될지 안될지 모르는데 평가 및 실행을 하면 자원 낭비이기 때문에 호출 즉 사용 될 때 평가 실행된다.(해당 과정은 실행컨텍스트를 배우면 확 이해가 된다)
왜 호이스팅이 일어날까?
일단 이번 수업을 통해 배운 것으로 이해한 것은 우리가 쓰는 Javascript가 가장 많이 쓰이는 곳은 웹 서비스이다.
리액트고 뷰고 다 Javascript의 프레임 워크로 결국 js 기반인데 이 웹페이지가 만들어지는 과정을 생각해본다면 이해가 확 온다
html
로 웹페이지를 작성하고 css
와 js
를 추가한다
그리고 그 3가지를 바탕으로 웹페이지가 그려지는 이때 기본적으로 우리는 서버에서 html
을 요청하게 되고 html
안에 있는 코드들을 통해 js
와 css
들을 받아온다
그럼 파일들이 도착했는데 js
가 실행되기 전이라면??
따라서 html
을 통해 화면은 그려졌지만 js
실행되기 전 에러를 낼 수 없고 미리 메모리 저장공간을 확보하기 위해 평가시 선언문들을 전부 위로 끌어올려 미리 undefined
나 <f.o>
,UIY
등으로 호이스팅 하는 것이다
라고 이해했다. JS를 만든 사람이 아는 것이라 너무 완벽한 만든 계기를 찾는 것보다 사용하는 살마으로써 JS의 호이스팅이 어떻게 진행되는지가 더 중요하긴 하다
console.log(i);
let i = 1;
console.log('x=', x);
var x = 1;
console.log(ff, f);
f(); // error
{
f();
var x = 2;
function f() {
console.log('f>', x, xx);
}
const b = 1;
}
function ff() {
console.log('ff>', y, yy);
}
if (x >= 2) {
var y = 5;
let yy = 55;
}
var xx = 100;
ff();
이 해당코드들이 호이스팅 되면 어떻게 될까?
let i = <notInitializedYet>;
var x = undefined;
f = undefined;
function ff() {
console.log('ff>', y, yy);
}
var y = undefined;
var xx = undefined;
//--------------------------------------------------
console.log(i)
// ... some code
이와 같이 선언문들이 쭉 위로 올라와서 호이스팅 된다.
근데 각각 다른 것들이 있고 '블럭 스코프에 있는 애들 중엔 어떤 애들은 올라오고 어떤 애들은 안올라 왔네' 하는 것이 보일것이다.
여기서 중요한 부분이 나오는데 ECMA 2015(ES6) 부터 많은 격변이 생겼는데 이때 let const
이 두개의 선언문이 나오게 된다
이 3가지는 무엇이 다를까?
우선 자주 들은 대로 let
과 const
둘 다 재 선언은 불가능 하며 let
은 값을 변경하여 할당할 수 있다.const
는 상수로 선언과 재할당 둘 다 불가능하다.
그럼var
는? 둘 다 가능하다
이정도로 인식하고 있을 사람이 많다.
가장 큰 부분이 다른데 각자 스스로가 포함되어 잇는 scope
를 가지는 게 다르다!
이부분이 가장 중요한데 let
과 const
는 block scope
를 가지고 var
는 function scope
를 가진다!
이는 중요한 개념 중 하나인데 JS는 함수형 프로그래밍 언어로 우리가 글로벌 오브젝트라고 글로벅 객체라 하는 부분도 실제로 따지면 함수라고도 볼 수 있다.
new 클래스
사용하는 것을 똑같이 함수로도 만들 수 있듯이 JS에서는 함수 스코프란 말이 자주 나오게 된다
그럼 이게 호이스팅과 무슨 연관이 있나? 알아보자
위에서 var는 함수 스코프를 가진다고 했다.
그럼
console.log(a); // undefined
console.log(b); // error
{
var a = 1;
}
function test(){
var b = 2;
}
a
와 b
의 다른 점은 무엇일까?
간단하다 a
는 블럭 스코프({ }
)에 존재하기 때문에 블럭을 벗어나서 그 상위로 올라오게 되고 그 상위가 마지막인 글로벌 스코프에 닿았기에 글로벌 오브젝트에 호이스팅 되어 undefined로 초기화 된 것이다
그러면 b
는??
b
가 선언된 부분에서 스코프를 한번 찾아보면 test
라는 함수 스코프가 딱 버티고 있는 것이 보일것이다
이게 바로 함수스코프를 가지는 var
인 것이다.
그럼 다시 저 위의 예시를 볼 때 궁금증을 가질 수 있다
왜? let과 const는 undefined
가 아닌가요?
이는 var는 메모리 선점과동시에 undefined
로 초기화를 해버리기 때문인데 이로인해 코드 순서가 꼬여도 에러가 발생되지 않는다
따라서 많은 문제점을 안고 있었고 이를 해결하기 위해 let
과 const
가 새롭게 등장한 것이다.
let
과 const
는 평가 당시 해당 블록 스코프를 벗어나지 않은 채로 호이스팅 된다
이로 인해 위 예시에서 if{ }
안에서 선언된 yy
가 가장 최상단으로 호이스팅 되지 않은것이다.
그럼 얘들은 호이스팅이 되는건가요?? 라고 생각한다면 된다.
해당 블록안에서 호이스팅 된다.
이는 평가 단계에서 자신이 속한 블록의 최상단으로 올라와 호이스팅이 되게 되는데 그러면
실제 if문 안에선 평가단계에
if(x >= 2 ){
let yy = <notInitializedYet>
}
이렇게 블럭 안에서 호이스팅 되어 있었을 것이다.
그럼 notInitializedYet
은 뭘까?
여기서 의견이 좀 나뉘는데 찾아보면 notInitializedYet
으로 초기화를 한 상태이다 라고 하는 분도 있고 아니다 초기화 되지 않은 상태다 라고 하는 사람도 있다.
따라서 나는 그냥 let
과 const
는! notInitializedYet
의 상태이다 라고 생각한다
Initialized가 초기화란 뜻으로 아직 초기화 되지 않음! 이란 말이긴 한데 이것자체도 이 상태를 줘야 가능하다 생각되는 편도 있다
따라서 let
과 const
는 var
의 undefined
와는 달리 notInitializedYet
상태로 호이스팅 하기 때문에 혹시나 code 순서가 엇갈렸다 하더라도 undefined
를 배출하여 디버깅을 힘들게 하지 않고 error를 배출하여 좀 더 실수를 줄일 수 있게 되었다
함수 또한 호이스팅 되는데 기본적으로 JS를 배울 때 함수는 불려 졌을 때 평가 실행 된다고 들었다.
다만 이와 같이 function
을 사용하여 전역에서 함수를 만들게 되면 전역에서 만들었기 때문에 JS는 평가 과정에서 아 전역 함수이기 때문에 자주 쓰이겠구나 해서 전역 오브젝트에 바로 올려놓게 된다.
그래서 현재 예시의 ff
함수는 전역에 선언된 것이기 때문에 글로벌 오브젝트에 올라가있는 상태라 아래에서 function
을 선언하여도 위에서 부를 수 있는것이다
실제로 상단에서 console.log(f)
를 실행시켜 보면 function
이 있는 것을 확인할 수 있다
물론 정말 평가 실행은 ff
함수가 불려질 때 된다. 글로벌 오브젝트에 올라가서 function object
에 함수가 등록되었다 해서 이 함수가 평가 실행 된 건 아니라는 뜻이다
그럼 f
는 왜 undefined
가 되어 있을까?
이는 블럭 스코프 안에서 선언되었기 때문이다.
기본적으로 블럭이란 스코프 안에서(함수또한 포함한다) 선언된 함수는 이너 함수라고도 하는데 이너 함수들은 호이스팅 당시 전역안에 있는 블럭이라 하더라도 if
등을 사용한 블럭일 수도 있고 해당 블럭을 실행 당시 무조건 통과한다는 보장을 평가단계에선 알 수가 없다.
따라서 굳이 function object에 등록하여 자원을 낭비하기 보단 해당 블럭을 실행할 때 호이스팅 해서 등록하겠다라는 동작 흐름인 것이다.
그럼 함수는 블럭 스코프인가요??
아니다. 함수스코프이다
undefined
로 올라온 상태인 것을 확인해 볼 수 있는데 블럭 스코프였다면 최상단으로 올라와 f = undefined
와 같은 호이스팅이 일어나질 않았을 것이다
그럼 블록스코프를 통과할 때 어떻게 될까?
{
function f() {
console.log('f>', x, xx);
}
//...
}
이와 같이 블럭을 들어온 순간 블럭 최상단으로 끌어올려져 이때 fucntion object에 등록되게 되는 것이다.
여기까지가 기본적인 hoisting인데 처음엔 이해하기 어려웠지만 꾸준히 공부하고 실행컨텍스트를 보며 많은 이해를 할 수 있었다.
아주 좋은 수업을 만나 확실한 javascript를 알게 되는 것 같아 기쁘다
Reference