Programming/JavaScript & TypeScript

호이스팅 (Hoisting)

Bonita SY 2020. 9. 15. 23:31
728x90
반응형

호이스팅 (Hoisting)

: 함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위(함수 블록{})의 최상단에 선언하는 것을 의미


- JavaScript 함수는 실행되기 전 함수 내에 필요한 변수값을 모두 모아 유효 범위의 최상단에 선언

 

호이스팅 절차

1. JavaScript parser가 함수 실행 전 해당 함수를 한번 훑음
2. 함수 안에 존재하는 변수/함수 선언에 대한 정보를 기억하고 있다가 실행
    ※ 실제로 코드가 위로 끌어올려지는 것이 아니라, JavaScript parser가 내부적으로 끌어올려서 처리
    ※ 실제 메모리 변화가 X

 

호이스팅의 대상 - var 변수 선언과 함수 선언문

- 할당은 끌어올려지지 않음
- let/const 변수 선언과 함수 표현식에서는 호이스팅 발생 X


※ 함수 표현식
- 변수에 할당된 함수 표현식은 변수의 scope 규칙을 따름
- 선언과 할당의 분리가 발생

 

간단한 예시 (var 변수 vs let/const 변수)

  var myname;            // [Hoisting O] 변수 선언
  myname = "HEEE";       // 할당
  let myname2 = "HEEE2"; // [Hoisting X]

 

간단한 예시 (함수 선언문 vs 함수 표현식)

  var foo2;				// [Hoisting O] 변수 선언

  function foo() {			// [Hoisting O] 함수 선언문
          console.log("hello");
  }

  foo();
  foo2();				// ERROR 발생

  foo2 = function() { 
          console.log("hello2");
  }

 

함수 선언문과 함수 표현식에서의 호이스팅

1. 함수 선언문에서 호이스팅

함수선언문은 코드를 구현한 위치에 관계없이 자바스크립트의 특징인 호이스팅에 따라 브라우저가 자바스크립트를 해석할 때 맨 위로 끌어 올려짐.

/* 정상 출력 */
function printName(firstname) { // 함수선언문 
    var result = inner(); // "선언 및 할당"
    console.log(typeof inner); // > "function"
    console.log("name is " + result); // > "name is inner value"

    function inner() { // 함수선언문 
        return "inner value";
    }
}

printName(); // 함수 호출 
/** --- JS Parser 내부의 호이스팅(Hoisting)의 결과 - 위와 동일 --- */
/* 정상 출력 */
function printName(firstname) { 
    var result; // [Hoisting] var 변수 "선언"

    function inner() { // [Hoisting] 함수선언문
        return "inner value";
    }

    result = inner(); // "할당"
    console.log(typeof inner); // > "function"
    console.log("name is " + result); // > "name is inner value"
}

printName(); 

=> 함수 선언문이 아래에 위치해도 printName 함수 내에서 inner를 function으로 인식하기 때문에 오류 발생 X

 

2. 함수 표현식에서 호이스팅

- 함수표현식은 함수선언문과 달리 선언과 호출 순서에 따라서 정상적으로 함수가 실행되지 않을 수 있음


함수표현식의 선언이 호출보다 위에 있는 경우 - 정상 출력

 /* 정상 */
 function printName(firstname) { // 함수선언문
     var inner = function() { // 함수표현식 
         return "inner value";
     }
        
     var result = inner(); // 함수 "호출"
     console.log("name is " + result);
 }

 printName(); // > "name is inner value"
 /* 정상 */
 /** --- JS Parser 내부의 호이스팅(Hoisting)의 결과 - 위와 동일 --- */
 function printName(firstname) { 
     var inner; // [Hoisting] 함수표현식의 변수값 "선언"
     var result; // [Hoisting] var 변수값 "선언"

     inner = function() { // 함수표현식 "할당"
         return "inner value";
     }
        
     result = inner(); // 함수 "호출"
     console.log("name is " + result);
 }
 
 printName(); // > "name is inner value"

 

함수표현식의 선언이 호출보다 아래에 있는 경우 (var 변수에 할당) - TypeError

 /* 오류 */
 function printName(firstname) { // 함수선언문
     console.log(inner); // > "undefined": 선언은 되어 있지만 값이 할당되어있지 않은 경우
     var result = inner(); // ERROR!!
     console.log("name is " + result);

     var inner = function() { // 함수표현식 
         return "inner value";
     }
 }
 printName(); // > TypeError: inner is not a function
 /** --- JS Parser 내부의 호이스팅(Hoisting)의 결과 --- */
 /* 오류 */
 function printName(firstname) { 
     var inner; // [Hoisting] 함수표현식의 변수값 "선언"

     console.log(inner); // > "undefined"
     var result = inner(); // ERROR!!
     console.log("name is " + result);

     inner = function() { 
         return "inner value";
     }
 }
 printName(); // > TypeError: inner is not a function

Q. printName에서 “inner is not defined” 이라고 오류가 나오지 않고, “inner is not a function”이라는 TypeError가 나오는 이유?


A. printName이 실행되는 순간, (Hoisting에 의해) inner는 ‘undefined’으로 지정되기 때문

- inner가 undefined라는 것은 즉, 아직은 함수로 인식이 되지 않고 있다는 것을 의미

 

함수표현식의 선언이 호출보다 아래에 있는 경우 (const/let 변수에 할당) - ReferenceError

 /* 오류 */
 function printName(firstname) { // 함수선언문
     console.log(inner); // ERROR!!
     let result = inner();  
     console.log("name is " + result);

     let inner = function() { // 함수표현식 
         return "inner value";
     }
 }
 printName(); // > ReferenceError: inner is not defined

 

inner에 대한 선언이 되어있지 않기 때문에 이때는 “inner is not defined” 오류가 발생

함수표현식보다 함수선언문을 더 자주 사용하지만, 어떤 코딩컨벤션에서는 함수표현식을 권장


즉, 어떤 컨벤션을 갖던지 한가지만 정해서 사용하는 게 좋음

 

호이스팅 우선순위

1. 같은 이름의 var 변수 선언과 함수 선언에서의 호이스팅
-  변수선언이 함수 선언보다 우선 순위가 높다.

 

예제)

  var myName = "hi";

  function myName() {
      console.log("yuddomack");
  }
  function yourName() {
      console.log("everyone");
  }

  var yourName = "bye";

  console.log(typeof myName);
  console.log(typeof yourName);

  /** --- JS Parser 내부의 호이스팅(Hoisting)의 결과 --- */
  // 1. [Hoisting] 변수값 선언 
  var myName; 
  var yourName; 

  // 2. [Hoisting] 함수선언문
  function myName() {
      console.log("yuddomack");
  }
  function yourName() {
      console.log("everyone");
  }

  // 3. 변수값 할당
  myName = "hi";
  yourName = "bye";

  console.log(typeof myName); // > "string"
  console.log(typeof yourName); // > "string"

 

2. 값이 할당되어 있지 않은 변수와 값이 할당되어 있는 변수에서의 호이스팅

  var myName = "Heee"; // 값 할당 
  var yourName; // 값 할당 X

  function myName() { // 같은 이름의 함수 선언
      console.log("myName Function");
  }
  function yourName() { // 같은 이름의 함수 선언
      console.log("yourName Function");
  }

  console.log(typeof myName); // > "string"
  console.log(typeof yourName); // > "function"

- 값이 할당되어 있지 않은 변수의 경우, 함수선언문이 변수를 덮어씀
- 값이 할당되어 있는 변수의 경우, 변수가 함수선언문을 덮어씀

 

호이스팅 사용 시 주의사항

- 코드의 가독성과 유지보수를 위해 호이스팅이 일어나지 않도록 한다
-- let과 const 사용
-- 함수와 변수를 가급적 코드 상단부에서 선언하면, 호이스팅으로 인한 scope 꼬임 현상 방지

 

출처

- gmlwjd9405.github.io/2019/04/22/javascript-hoisting.html

728x90
반응형