[JavaScript] Hoisting

smiley·2022년 4월 25일
0

JavaScript

목록 보기
4/6
post-thumbnail

Hoisting

호이스팅(hoisting)이란 var 선언문이나 function 선언문 등을 해당 scope의 선두로 옮긴 것처럼 동작하는 특성을 말한다.

자바스크립트는 ES6에서 도입된 let, const를 포함하여 모든 선언(var, let, const, function, class)을 호이스팅한다.


📌우선 변수가 생성되는 과정을 자세히 살펴보자.

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

🌼 var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다.

// scope의 선두에서 선언 단계와 초기화 단계가 실행된다. 
console.log(foo); // undefined

var foo; //  변수 선언문
console.log(foo); // undefined

foo = 1; // 할당문 : 할당 단계
console.log(foo); // 1

👉 var은 scope에 변수를 등록(선언 단계)하고 메모리에 변수를 위한 공간을 확보한 뒤, undefined로 초기화(초기화 단계)한다.
👉 변수 선언문 이전에 변수에 접근하여도 scope에 변수가 존재하기 때문에 에러가 발생하지 않는다. 이러한 현상을 '변수 호이스팅'이라 한다.


🌼 let 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다.

// scope의 선두에서 선언 단계만 실행된다.
console.log(foo); // Uncaught ReferenceError: foo is not defined

let foo; // 변수 선언문 : 초기화 단계
console.log(foo); // undefined

foo = 1; // 할당문 : 할당 단계
console.log(foo); // 1

👉 let은 scope에 변수를 등록(선언 단계)하지만, 초기화 단계는 변수 선언문에 도달했을 때 이루어진다.
👉 초기화 이전에 변수에 접근하려고 하면 참조 에러(Reference Error)가 발생한다. 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문이다.
👉 scope의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없으며, 해당 구간을 '일시적 사각지대(Temporal Dead Zone; TDZ)'라고 부른다.


📚 예제 1 ) 매개변수와 변수에 대한 호이스팅

function foo(x) {
  console.log(x); // (1)
  var x;
  console.log(x); // (2)
  var x = 2;
  console.log(x); // (3)
}
foo(1);

이때 (1), (2), (3) 에서 어떤 값들이 출력될까?

우선, 함수를 호출하면서 '1'이라는 인자가 전달된 것을, 함수 내부에서 변수를 선언한 것과 같이 표현할 수 있다.

function foo() {
  var x = 1;
  console.log(x); // (1)
  var x;
  console.log(x); // (2)
  var x = 2;
  console.log(x); // (3)
}
foo();

이제, 선언문만 끌어올리고 할당문은 원래 자리에 남겨두면 다음과 같다.

function foo() {
  var x;
  var x;
  var x;
  
  x = 1;
  console.log(x); // (1)
  console.log(x); // (2)
  x = 2;
  console.log(x); // (3)
}
foo();

결과적으로, (1) 1, (2) 1, (3) 2 가 출력된다.


📚 예제 2 ) 함수 선언의 호이스팅

function foo() {
  console.log(b); // (1)
  var b = 'bbb';
  console.log(b); // (2)
  function b() {}
  console.log(b); // (3)
}
foo();

이때 (1), (2), (3) 에서 어떤 값들이 출력될까?

선언문만 끌어올리고 할당문은 그대로 남겨두자.

function foo() {
  var b;
  function b() {};
  
  console.log(b); // (1)
  b = 'bbb';
  console.log(b); // (2)
  console.log(b); // (3)
}
foo();

이때, 함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것으로 볼 수 있다.

function foo() {
  var b;
  var b = function b() {};
  
  console.log(b); // (1)
  b = 'bbb';
  console.log(b); // (2)
  console.log(b); // (3)
}
foo();

결과적으로, (1) b 함수, (2) 'bbb', (3) 'bbb' 가 출력된다.


📚 예제 3 ) 함수 선언문과 함수 표현식의 호이스팅

console.log(sum(1, 2)); // (1)
console.log(multiply(3, 4)); // (2)

function sum(a, b) {
  return a + b;
}

var multiply = function(a, b) {
  return a * b;
}

이때 (1), (2) 에서 어떤 값들이 출력될까?

함수 선언문의 경우 전체가 hoisting되지만, 함수 표현식은 변수의 선언문만 hoisting된다.

var sum = function sum(a, b) {
  return a + b;
}
var multiply; // 선언문

console.log(sum(1, 2)); // (1)
console.log(multiply(3, 4)); // (2)

multiply = function(a, b) { // 할당문
  return a * b;
}

결과적으로, (1) 3, (2) 'multiply is not a function'이 출력된다.


출처 )
PoiemaWeb : block & scope
정재남(2019), [코어 자바스크립트], 위키북스

profile
하루하루 성장하는 개발자입니다. 배운 것을 기록하고 있습니다.

0개의 댓글