hoisting
끌어 올리기
호이스팅이란?
코드는 물리적으로 위에서 아래로 작성된다.
때문에 기본적으로 코드는 '순서대로' 작동하지만 자바스크립트에서는 변수 및 함수 선언이 해당 스코프의 최상단으로 '끌어올려진 것처럼 동작하는' 현상인 호이스팅(Hoisting)이 발생한다.
즉, 코드의 실행 순서와는 상관없이 변수 및 함수 선언이 스코프 상단에서 선언된 것처럼 동작한다고 해서 호이스팅이라고 부른다.
간단하게 말하면 자바스크립트에서 일정 스코프(지역 범위든 전역 범위든) 안에 선언된 모든 변수는 해당 범위의 전체에 걸쳐 유효하다는 의미이다.
이는 자바스크립트가 인터프린터 언어이기 때문에 가지는 특성이기도 한데 인터프리터 언어는 코드를 실행하기 전에 어느 정도의 사전 처리가 필요하며, 호이스팅은 이러한 처리를 수행하는 과정에서 발생하는 현상이다.
인터프리터 언어란(Interpreter)?
인터프리터(Interpreter)는 프로그래밍 언어로 작성된 소스 코드를 한 줄씩 읽어 들여 즉시 실행하는 프로그램이다.
in·ter·pret·er
통역사, 연주자, 해석 프로그램
인터프리터의 작동 방식은 다음과 같다.
- 소스 코드 입력 : 사용자가 작성한 소스 코드를 입력받는다.
- 코드 해석 : 인터프리터는 소스 코드를 한 줄씩 읽어 들여 해당 줄의 코드를 해석한다.
- 즉시 실행 : 해석된 코드를 즉시 실행한다.
- 다음 줄 해석 : 다음 줄의 코드를 읽어 들여 해석하고 실행하는 과정을 반복한다.
간단하게 말하면 1줄 읽고 1줄 실행하고 1줄 읽고 1줄 실행한다는 소리다.
호이스팅이 사용되는 이유와 필요성
하지만 이렇게 1줄씩 순서대로 실행하게 되면 반드시 문제가 생기게 된다. 즉 모든 단계가 정확히 순서대로 작성되야 한다는 것이다. 이후에 어떤 코드를 추가할 줄 알고 일일히 순서대로 코드를 작성한단 말인가?
때문에 자바스크립트에서는 코드의 유연성과 실행 효율을 높이기 위해서 코드를 한 줄씩 읽어 실행하기 전에 전체 코드를 스캔하여 선언된 변수와 함수를 메모리에 미리 등록한다. 그 다음 각 선언이 적용되야하는 코드에서 불러와서 사용한다.
때문에 선언은 실제 코드의 위치와 상관없이 스코프의 최상단으로 끌어올려진 것처럼 동작하게 되는데, 이를 호이스팅이라고 부르는 것이다.
하지만 변수의 값은 호이스팅 되지 않는다.(변수의 생성 단계)
기본적으로 변수의 생성은 아래 3가지 단계를 거친다.
- 선언 단계 (Declaration Phase)
- 변수의 이름을 등록하는 단계
- 초기화 단계 (Initialization Phase)
- 변수에 메모리 공간을 할당하고 undefined로 초기화하는 단계
- 할당 단계 (Assignment Phase)
- 변수에 실제 값을 할당하는 단계
그런데 호이스팅은 변수가 선언되었다는 사실만을 메모리에 등록한다. 때문에 값은 가져오지 않는다.
호이스팅 시 값을 가져오지 않는 이유
- 값 충돌 방지 : 변수의 값은 다양하게 할당 될 수 있다. 그런데 변수의 값도 모두 호이스팅해버리면 값이 충돌하거나 하나의 변수에 계속 다른 값이 할당되는 문제가 발생 할 수 있다.
- 코드 실행 순서 유지 : 값을 가져올 경우 코드의 실제 실행 순서와 달라져 예측 불가능한 동작이 발생할 수 있다. 5*3+5를 계산하려고 했는데 5*5+3이 계산될 수도 있다.
- 예측 가능성 : 결과적으로 코드의 예측 가능성을 지켜서 오류가 발생하지 않고 개발자가 의도한 대로 코드가 실행되도록 하기 위함이다.
var, let, const, 함수의 호이스팅 방식
호이스팅은 변수와 함수를 대상으로 이루어지지만 호이스팅이 되는 방식은 각기 다르다.
1. var 호이스팅
- var로 선언된 변수는 함수 스코프를 가지며, 호이스팅 시 값이 undefined로 초기화된다.
- 즉, 선언은 스코프 최상단으로 끌어올려지지만, 실제 값의 할당은 원래 코드 위치에서 이루어진다.
- 이로 인해 선언 전에 변수를 사용하더라도 ReferenceError가 발생하지 않고 undefined 값을 반환하며, 오류가 발생하지 않는다.
console.log(myVar); // undefined
var myVar = 10;
console.log(myVar); // 10
하지만 오류가 발생하지 않는것이지 제대로 작동하는 것은 아니다. 때문에 결국 코드의 순서를 잘 지켜줘야한다.
2. let, const 호이스팅
let과 const의 호이스팅 공통점
- 블록 스코프 (Block Scope) : 변수의 유효 범위가 선언된 블록({}) 내부로 제한된다.
- 선언 단계와 초기화 단계 분리
- let과 const는 선언 단계와 초기화 단계가 분리되어 이루어진다.
- 선언 단계에서는 변수가 스코프에 등록되지만, 초기화 단계 이전에는 접근할 수 없다.
- 이 구간을 '일시적 사각지대(Temporal Dead Zone, TDZ)'라고 부른다.
- 초기화되지 않음
- 호이스팅 시 undefined로 초기화되지 않는다.
- 따라서 선언 전에 변수에 접근하면 ReferenceError가 발생한다.
let과 const의 호이스팅 차이점
- let : 변수의 값을 재할당할 수 있다.
- const : 선언과 동시에 초기화해야 하며, 값을 변경할 수 없다.
var과 let, const 호이스팅의 차이점
앞서 let과 const은 초기화 되지 않기 때문에 선언 전에 변수에 접근하면 ReferenceError가 발생한다고 했는데 이는 var과의 차이점이기도 하다. var은 호이스팅 과정에서 변수가 자동으로 초기화되어 undefined값을 가진다. 때문에 값은 제대로 할당되지 않을지언정 오류가 발생하지는 않는다.
하지만 let과 const은 선언은 호이스팅되지만 변수는 초기화 되지 않고 아예 아무런 값도 할당되지 않기 때문에 ReferenceError가 발생하는 것이다. 때문에 let과 const는 무조건 값이 할당 된 이후에야 오류가 발생하지 않는다.
만약에 아무 것도 지정되지 않은 변수를 참조하게 된다면 프로그램에 심각한 문제가 생길 수도 있을 것이다.
TDZ(일시적 사각 지대, Temporal Dead Zone)
때문에 자바 스크립트에서는 코드의 안전성 확보와 개발자의 오류 감지를 목적으로 TDZ라는 개념을 도입했다.
TDZ는 변수가 선언되었지만 아직 초기화되지 않은 상태에서 잘못된 접근을 방지하기 위해 변수로의 접근을 막는 구간이다.
let
console.log(myLet); // ❌ ReferenceError (초기화 전, TDZ 상태)
let myLet;
console.log(myLet); // ✅ undefined (초기화 완료)
위 코드의 경우
1번째 줄 : let 변수 자체는 호이스팅을 통해서 선언되었지만 아직 초기화 전이기 때문에 console.log()로 값을 호출할 경우 TDZ 상태여서 오류가 뜨게 된다.
2번째 줄 : let은 변수만을 선언언해도 초기화가 동시에 이루어 진다. 즉 변수의 값에 undefined가 할당된다.
3번째 줄 : console.log(myLet); 은 변수에 할당된 undefined를 출력한다.
const
console.log(myConst); // ❌ ReferenceError (TDZ 상태)
const myConst = 10; // ✅ 선언과 동시에 초기화해야 함
console.log(myConst); // ✅ 10
위 코드의 경우
1번째 줄 : const 변수 자체는 호이스팅을 통해서 선언되었지만 아직 초기화 전이기 때문에 console.log()로 값을 호출할 경우 TDZ 상태여서 오류가 뜨게 된다.
2번째 줄 : const 변수는 별도로 초기화만 진행하는 것은 불가능하고 변수의 값도 함께 선언해줘야 한다. 즉 변수의 값에 특정 값이 할당된다.
3번째 줄 : console.log(myLet); 은 변수에 할당된 특정 값을 출력한다.
때문에 사실상 let과 const의 TDZ는 실질적으로는 동일 범위이다. 초기화가 진행되지 않은 선언 상태에서만 TDZ이기 때문인데 let은 초기화 과정에서 undefined 값을 할당 받아 오류가 나지 않고 const는 초기화와 할당을 무조건 동시에 진행하기 때문이다.
3. 함수 호이스팅
- 함수 선언 전체가 호이스팅된다. 즉, 함수 선언 전에 함수를 호출해도 정상적으로 실행된다.
- 함수 표현식은 변수 호이스팅과 동일하게 동작한다.
간단하게 말하면 함수는 통째로 호이스팅되는 것이다. 때문에 함수를 사용하기 위해 함수 내부에 있는 변수에 다시 초기화와 할당을 해야만 함수를 사용할 수 있는 것이 아니다.
myFunction(); // 함수 호출 (선언 전에 호출 가능)
function myFunction() { // 함수 선언
console.log("Hello, hoisting!");
}
함수 표현식
하지만 함수가 아니라 함수 표현식이라면 얘기가 조금 달라진다.
console.log(myFunction); // undefined
myFunction(); // TypeError: myFunction is not a function
var myFunction = function() {
console.log("함수 표현식 호출");
};
- 형태는 함수와 비슷해 보이지만 결국 변수를 호출하는 것과 동일하기 때문에 변수 호이스팅과 동일하게 작동한다.
- 익명 함수를 변수에 할당하는 방식이다.
- 즉 위와 같은 코드라면 function() { console.log("함수 표현식 호출"); };까지가 통째로 변수의 값이다.
'JavaScript > 자바 스크립트 기초' 카테고리의 다른 글
자바 스크립트 기초(6) - 연산자 (0) | 2025.03.18 |
---|---|
자바 스크립트 기초(5) - 동적 언어, 타입 변환 (0) | 2025.03.18 |
자바 스크립트 기초(4) - 데이터 타입: 원시 타입, 참조 타입 (0) | 2025.03.18 |
자바 스크립트 기초(2) - 스코프(Scope)란? : 변수의 참조 범위 (0) | 2025.03.17 |
자바 스크립트 기초(1) - 변수(variable)란? (0) | 2025.03.17 |