[JavaScript] 호이스팅과 var/let/const 차이

 

01. JS 변수 선언 방법 : var / let / const

자바스크립트에서 사용되는 변수 선언 방식에는 var, let, const 3가지가 있다.

var는 ES6이전인 비교적 오래전부터 사용되었지만, ES6 등장 이후 현재는 잘 사용되지 않는 변수 선언 방법이다.

현재는 거의 let, const를 사용하며, var, let, const의 차이점은 아래와 같다.

  재선언 재할당 스코프 호이스팅
var O (재선언시 덮어쓰기) O 함수 스코프 호이스팅 발생 
with Initialization
let X O (가변 변수) 블록 스코프 호이스팅 발생 
without Initialization 
(Reference Error)
const X X (불변 변수) 블록 스코프 호이스팅 발생 
without Initialization 
(Reference Error)

 

var는 재선언이 가능한데, 이는 동일한 명칭으로 선언된 변수를 의도치않게 변경 가능하다는 의미이다.

var num = 10
var num = 20

var의 재선언 예시는 위와 같다. 하지만 var 대신 let이나 const를 사용하여 위와 같이 재선언하는 것을 불가능하다.

 

let num = 10
num = 20

let은 위와 같이 재할당은 가능하다. 하지만 const는 재할당, 재선언이 모두 불가능하다.

따라서 let은 값이 변할 수 있을 때, const는 변하지 않는 값을 선언할 때 사용해야 한다.

 

 

🎯 var, let, const는 모두 호이스팅이 발생한다.

  • var - 호이스팅 발생 (초기화 포함): var로 선언된 변수는 스코프의 최상단으로 끌어올려지지만, 초기화된 상태(undefined)로 호이스팅. 즉, 선언 전에 변수를 참조 가능.
  • let - 호이스팅 발생 (초기화 없음): let으로 선언된 변수도 스코프의 최상단으로 끌어올려지지만, 초기화되지 않은 상태로 호이스팅. 따라서 선언 전에 변수를 참조하면 ReferenceError가 발생.
  • const - 호이스팅 발생 (초기화 없음): const도 스코프의 최상단으로 끌어올려지지만, 초기화되지 않은 상태로 호이스팅. 선언 전에 변수를 참조하면 ReferenceError가 발생.

 

호이스팅에 관련된 내용은 아래에서 더 자세히 다룬다.

 

 

 

02. 스코프 : 변수에 대한 접근 영역

#1. 함수 스코프

함수 스코프는 함수 생성 시 스코프 영역이 생긴다.

따라서, 변수가 함수 내에서 선언되었을 때, 해당 변수가 함수 전체에서 유효하다.

자바스크립트에서 var를 사용하여 변수를 선언하면, 그 변수는 함수 스코프를 따르게 된다.

 

function example() {
    var x = 10;

    if (true) {
        var y = 20;
        console.log(x); // 10 (함수 내 어디서든 접근 가능)
    }

    console.log(y); // 20 (if 블록 외부에서도 접근 가능)
}

example();
// console.log(x); // ReferenceError: x is not defined

 

위 코드에서 var로 선언된 x와 y는 함수 example 내에서만 유효하다. 따라서 함수 내부 어디서든 x와 y에 접근할 수 있지만, 함수 밖에서는 접근할 수 없다. 만약 함수 외부에서 console.log(x)로 x에 접근한다면 RefereceError: x is not defined라는 에러가 출력될 것이다.

 

 

💡 함수 스코프 특징

  • var로 선언된 변수는 선언된 위치에 관계없이 함수 내에서 접근 가능
  • if, for, while 등의 블록 내부에서 var로 선언된 변수도 함수 전체에서 유효
  • 함수가 호출되면 스코프가 생성되고, 함수가 종료되면 스코프도 사라짐

 

 

 

#2. 블록 스코프

블록 스코프는 변수가 { } 로 감싸진 블록 내에서만 유효하다.

let과 const로 선언된 변수는 블록 스코프를 따르고, ES6부터 도입되었다.

function example() {
    let x = 10;

    if (true) {
        let y = 20;
        console.log(x); // 10 (블록 내에서 접근 가능)
        console.log(y); // 20 (블록 내에서 접근 가능)
    }

    console.log(x); // 10 (함수 내에서 접근 가능)
    // console.log(y); // ReferenceError: y is not defined (y는 블록 외부에서 접근 불가)
}

example();

 

위 코드에서 let으로 선언된 y는 if 블록 내부에서만 유효하며, 블록 외부에서는 접근할 수 없다. 반면, x는 함수 내에서 선언되었으므로 함수 전체에서 유효하다. 만약 함수 안이지만 if 문 밖에서 console.log(y)를 통해 y를 접근하면 ReferenceError가 발생할 것이다.

 

 

💡 블록 스코프 특징

  • let과 const로 선언된 변수는 선언된 블록( { } ) 내에서만 유효
  • 블록이 종료되면 스코프도 종료되며, 해당 블록 스코프 내에서 선언된 변수는 더이상 접근 불가
  • 블록 스코프는 제어문뿐만 아닌 일반적인 { }로 묶인 코드에서도 적용

 

 

 

 

03. 호이스팅

#1. 호이스팅이란?

자바스크립트에서 호이스팅이란 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다.

var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화한다.

반면, let과 const로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않는다.

 

즉, 간단히 말하면 호이스팅 단어의 의미처럼 스크립트 내 변수와 함수의 선언 순서에 상관 없이 순서가 끌어올려진 듯한 현상이다.

이는 자바스크립트 엔진이 먼저 전체 코드를 한 번 스캔하고 실행 컨텍스트에 미리 기록해놓기 때문에 이런 현상이 발생하는 것이다.

 

자바스크립트 엔진의 수행 방식에 따른 함수 실행 원리는 다음 포스팅에서 더 자세히 다룬다.

https://itguswjd.tistory.com/207

 

[JavaScript] 자바스크립트 내 함수 동작원리 : 실행 컨텍스트(Creation Phase / Execution Phase)

자바스크립트 엔진의 파일 구동 방식은 자바스크립트 함수 구동 방식과 동일하게 처리한다.변수는 선언과 할당, 함수는 선언과 실행이라는 두 가지 주요 작업을 처리한다.자바스크립트 엔진의

itguswjd.tistory.com

 

 

 

 

 

#2. 함수 호이스팅

test()

function test() {
  console.log('hoisting')
}

test()

 

위 코드를 보면 첫번째 test()는 함수 선언 전에 호출되고 있다. 

함수를 할당하는 function test() 코드가 함수를 실행하는 test() 코드보다 아래에 작성된 경우, 첫번째 라인의 test()는 실행되지 않을 것이라 생각할 수 있다.

하지만 자바스크립트는 함수를 호이스팅하기 때문에 최상단의 test() 코드는 정상적으로 작동되어 콘솔에 hoisting이 출력된다.

 

 

 

 

#3. 변수 호이스팅

 

◼︎ var로 선언한 변수 호이스팅

var로 선언한 변수는 선언, 초기화 단계가 동시에 실행된다. 

즉, 변수가 선언되면 동시에 초기화가 이루어져 undefined가 할당된다.(이 초기화 단계에서 변수는 자동으로 undefined라는 값을 가지게 된다. 이 초기화는 개발자가 의도적으로 값을 할당하는 것과는 다르다.)

console.log(name); // undefined
var name = "guswjd";
console.log(name); // guswjd

var 키워드로 선언된 변수는 호이스팅에 의해 코드 최상단으로 선언이 끌어올려진다.

하지만 할당은 호이스팅 되지 않는다. 위 코드를 실행하면 다음과 같이 동작하게 된다.

  1. 자바스크립트는 코드를 실행하기 전 var name; 선언을 최상단으로 끌어올린다.
  2. 첫번째 console.log(name);은 아직 값이 할당되지 않은 상태에서 undefined를 출력한다.
  3. 이후 name = "guswjd" 할당이 실행된다.
  4. 두번째 console.log(name)에서는 name에 할당이 되었으므로 "guswjd"이 출력된다.

 

 

 

 

◼︎ let, const로 선언한 변수 호이스팅

let, const는 var와 달리, 선언 단계와 초기화 단계가 분리되어 실행된다.

따라서, 변수가 선언되기 전에 접근하려고 하면 ReferenceError가 발생한다.

이는 let, const가 호이스팅되지 않는 것이 아닌, 호이스팅되지만 초기화되지 않은 상태로 존재하기 때문이다.

console.log(name); // ReferenceError
const name = "guswjd";
console.log(name); // guswjd

console.log(age); // ReferenceError
let age = 25;
console.log(age); // 25
  1. let, const는 호이스팅이 발생하지만, 초기화되지 않은 상태로 존재한다.
  2. 초기화되지 않은 상태에서 해당 변수에 접근하려 하면 ReferenceError가 발생한다.
  3. 초기화된 후에는 정상적으로 접근 가능하다.

 

let과 const는 TDZ의 영향을 받는데, 선언 단계와 초기화 단계 사이에서 변수를 참조할 경우 TDZ로 인해 레퍼런스 에러가 발생한다.

 

 

 

 

💡 TDZ(Temporal Dead Zone)

TDZ는 자바스크립트에서 let, const로 선언된 변수가 호이스팅되지만, 초기화되기 전까지 존재하는 일시적인 영역이다.

이 영역에서 변수를 참조하려고 하면 ReferenceError가 발생한다.

 

console.log(name); // ReferenceError
let name = "guswjd";
console.log(name); // "guswjd"

 

  1. 자바스크립트는 let name; 선언을 호이스팅하지만 초기화 하지 않는다. 
  2. 초기화되기 전까지 name은 TDZ에 놓이게 된다.
  3. 첫번째 console.log(name)이 실행될 때, name은 아직 초기화되지 않은 상태로, TDZ 영역에 있는 name을 참조하고 있기 때문에 ReferenceError가 발생한다.
  4. let name = "guswjd"에 도달하면서 name 변수가 초기화된다.
  5. 이후 console.log(name)이 실행될 때 name에 정상적으로 접근 가능하다.

 

반응형