Published on

호이스팅

Authors
  • avatar
    Name
    Na Hyunwoo
    Twitter

다음과 같은 두 개의 코드가 있습니다.

function callName() {
  console.log(name)
  var name = 'hyunwoo'
}

callName()
function callName() {
  console.log(name)
  let name = 'hyunwoo'
}

callName()

위의 코드는 undefined를 출력하고, 밑의 코드는 Uncaught ReferenceError: Cannot access ‘name’ before initialization을 출력합니다.

분명 두 개의 callName 함수 모두 변수에 값이 할당되기 이전에 name을 출력하려 하는데 왜 다른 메세지가 출력이 될까요 ? 여기서 호이스팅이라는 개념에 대한 이해가 필요합니다.

호이스팅-Hoisting

hoisting-dictionary

Hoisting은 단어 그대로 끌어올린다는 뜻이 있습니다. 코드 하단에서 선언되는 변수나 함수를 끌어올려서 코드 상단에서 미리 선언해준다는 것을 의미합니다.

코드 하단에서 선언되는 변수나 함수를 끌어올려서 미리 ‘선언’ 해준다고 ? 우리는 여기서 선언이라는 단어에 집중해야 합니다.

우리가 선언이라는 단어를 이해하기 위해선 변수의 생명 주기를 이해해야 합니다.

변수 생명주기

hoisting-variables-lifecycle

변수의 생명 주기는 다음과 같습니다.

  1. 선언 단계 (Declaration phase)
    • 변수를 실행 컨텍스트변수 객체에 등록하는 단계입니다.
    • 이 변수 객체는 스코프가 참조하는 대상이 됩니다.
  2. 초기화 단계 (Initialization phase)
    • 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보합니다.
    • 이 단계에서 변수는 undefined로 초기화됩니다.
  3. 할당 단계 (Assignment phase)
    • undefined로 초기화된 변수에 실제 값을 할당합니다.

호이스팅은 코드 아래에 있는 변수와 함수를 상단부로 끌어올려 선언해주는 녀석입니다.

변수 호이스팅

let과 const의 호이스팅

다시 위의 코드로 돌아가봅시다.

function callName() {
  console.log(name)
  let name = 'hyunwoo'
}

callName() //reference error

callName 함수가 호출이 되었을 때 호이스팅이 일어나 name 변수가 선언됩니다. 선언은 단지 변수를 자바스크립트 엔진에 알리는 것입니다. 따라서 아직 초기화가 일어나지 않은 name을 참조하려고 할 때 Reference Error가 발생합니다. 이처럼 선언만 이루어지고 초기화가 이루어지지 않은 이 간극을 TDZ(Temporal Dead Zone)라고 부릅니다.

TDZ

TDZ(Temporal Dead Zone)은 말 그대로 일시적인 사각지대 입니다. 스코프 시작 지점에서 초기화 시작 지점까지를 TDZ라고 합니다.

hoisting-TDZ

var의 호이스팅

반면에 var 키워드로 선언된 변수는 아래 그림과 같이 선언 단계와 초기화 단계가 한번에 이뤄집니다. 만약 선언 단계와 초기화 단계가 동시에 이뤄진다면 어떻게 될까요 ? 그 과정을 한번 살펴봅시다.

hoisting-var-lifecycle
function callName() {
  console.log(name)
  var name = 'hyunwoo'
}

callName() // undefined

callName() 함수를 호출하면 함수 내부에 있는 변수, 함수에 대해서 호이스팅이 일어납니다. 호이스팅은 변수를 선언해 자바스크립트 엔진에게 변수가 있음을 알립니다. 그런데 var은 let, const와 다르게 선언과 동시에 초기화가 진행됨에 따라 name에는 undefined라는 값이 할당됩니다. 따라서 위의 코드를 실행하면 콘솔에는 undefined가 찍힙니다.

함수 호이스팅

hoisting-function-lifecycle

함수는 선언, 초기화, 할당 단계가 모두 함께 진행됩니다. 따라서 함수는 선언된 위치에 종속되지 않고 모든 위치에서 호출될 수 있습니다.

function sumArray(array) {
  return array.reduce(sum)

  function sum(a, b) {
    return a + b
  }
}
sumArray([5, 10, 8]) // => 23

함수의 호이스팅은 변수 호스팅보다 가장 먼저 이루어집니다. 함수 호이스팅은 함수 선언식에만 해당하는데 이는 좀만 생각해보면 당연한 결과입니다. 함수표현식은 결국 변수에 함수를 할당하는 모양이기 때문에 이것을 변수의 호이스팅으로 볼 수 있기 때문입니다.

함수 선언식 - Function Declarations

일반적인 프로그래밍 언어에서의 함수 선언과 비슷한 형식이다.

function funcDeclarations() {
  return 'A function declaration'
}

함수 표현식 - Function Expressions

유연한 자바스크립트 언어의 특징을 활용한 선언 방식이다.

const funcExpression = function () {
  return 'A function expression'
}

결론

var은 변수를 선언하고 호출하는 것의 자유도를 높였지만 에러가 발생하기 쉬운 환경을 만들었습니다. 따라서 호이스팅이 일어나지 않도록 코드를 짜는 것은 중요합니다. 호이스팅으로 파생되는 많은 문제들을 방지하기 위해 아래의 사항들을 지킬 수 있도록 합시다.

코드의 가독성과 유지보수를 위해 호이스팅이 일어나지 않도록 한다.

  • 함수와 변수를 가급적 코드 상단부에서 선언하고 초기화하여 사용하자. 이를 통해 호이스팅으로 인한 스코프 꼬임 현상을 방지할 수 있다.
  • let과 const를 사용한다.

var을 쓰면 혼란스럽고 쓸모없는 코드가 생길 수 있다. 그런데 왜 var과 호이스팅을 이해해야 할까 ?

  • 아직 var로 짜여진 코드들이 있고, 그런 코드들을 이해하거나 새로운 문법으로 바꾸고자 하려면 var에 대해서 이해하고 있어야 한다.
  • 따라서 var이 어떻게 동작하고 호이스팅은 어떤 것인지 이해하고 있어야 한다.

참고

스코프

변수의 스코프(변수의 유효범위)는 지역 변수이냐 전역 변수인지에 따라 달라집니다.

지역 변수

지역 변수란 함수 내에서 선언된 변수를 가리킵니다. 따라서 변수가 선언된 함수 내에서만 유효하며, 함수가 종료되면 메모리에서 사라집니다. 함수의 매개변수도 함수 내에서 정의되는 지역 변수처럼 동작합니다.

전역 변수

전역 변수란 함수의 외부에서 선언된 변수를 가리킵니다. 전역 변수는 프로그램의 어느 영역에서나 접근할 수 있으며, 웹 페이지가 닫혀야만 메모리에서 사라집니다.

레퍼런스

호이스팅에 대한 오해와 진실

JavaScript Variables Lifecycle: Why let Is Not Hoisted

TDZ을 모른 채 자바스크립트 변수를 사용하지 말라

함수 표현식 vs 함수 선언식