WEB/Javascript

[Javascript] 호이스팅(Hoisting)

dev-olive 2022. 5. 9. 11:31

호이스팅(Hoisting)

호이스팅(Hoisting)의 개념

함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위의 최상단에 선언하는 것을 의미한다.

호이스팅이란

  • 자바스크립트 함수는 실행되기 전에 함수 안에 필요한 변수 값들을 모두 모아서 유효 범위의 최상단에 선언한다.
    • 자바스크립트 Parser가 함수 실행 전 해당 함수를 한 번 훑는다.
    • 함수 안에 존재하는 변수/함수 선언에 대한 정보를 기억하고 있다가 실행시킨다.
    • 유효범위: 함수 블록 {} 안에서 유효
  • 함수 내에서 아래 쪽에 존재하는 내용 중에서 필요한 값들을 자바스크립트 Parser 내부적으로 끌어올리는 것
  • 자바스크립트 변수 생성과 초기화(선언과 할당)가 분리되어 진행되기 때문에 호이스팅이 발생한다.

호이스팅의 대상

  • var 변수 선언함수선언문에서만 호이스팅이 일어난다.

    • var 변수/ 함수의 선언만 위로 끌어 올려지며, 할당은 끌어올려지지 않는다.
    • let/const 또한 발생하지 않는 것처럼 보일 뿐 호이스팅이 일어나긴한다.
      다만 var 는 선언과 초기화가 동시에 일어나는 반면, let/const는 선언이 일어난 후 해당 코드가 실행되기 전까지는 초기화가 되지 않아 호이스팅이 일어나지 않는 것처럼 보인다.
  • var vs let/const

    console.log("test");
    var name = "KIM"; 
    let name2 = "KIM2";

    JS parser 내부에서 발생하는 Hoisting

    var name; // *Hoisting* 선언
    console.log("test");
    myname = "KIM"; // 할당
    let myname2 = "KIM2"; // *Hoisting* 발생 x
  • 함수선언문 vs 함수표현식

    func1();
    func2();
    
    function func1(){ // 함수 선언문
        console.log("hello");
    }
    var func2 = function(){ // 함수 표현식
        console.log("hello2");
    }
    var func2; // *Hoisting* 함수 표현식의 변수값 선언
    function func1(){ // *Hoisting* 함수선언문
        console.log("hello");
    }
    func1();
    func2(); //ERROR
    func2 = function(){
        console.log("hello2");
    }

    변수에 할당된 함수표현식은 끌어 올려지지 않기 떄문에 이때는 변수의 스코프 규칙을 그대로 따른다.

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

함수선언문에서의 호이스팅

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

    function func(testParam){ // 함수선언문
        var result = foo(); // 선언 및 할당
        console.log("example of"+ result); // "example of hoisting"
    
        function foo(){
            return "hoisting";
        }
    }
    func(); //함수 호출
    function func(testParam){
        var result; // *Hoisting* var 변수 선언
    
        function foo(){ // *Hoisting* 함수선언문
            return "hoisting";
        }
        result = foo();
        console.log("example of"+ result); // "example of hoisting"
    }
    printName();

    위의 경우 함수선언문이 아래에 있어도 func 함수 내에서 foo를 function으로 인식하기 때문에 오류가 발생하지 않는다.

함수표현식에서의 호이스팅

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

    • 함수표현식에서는 선언과 할당의 분리가 발생한다.
  • 함수표현식의 선언이 호출보다 위에 있는 경우 :: 정상

    function func(testParam) { // 함수선언문
        var foo = function() { // 함수표현식
            return "hoisting";
        }
        var result = foo(); // 함수 호출
        console.log("example of " + result);
    }
    func(); // "example of hoisting"
    function func(testParam) {
        var foo; // *Hoisting* 함수표현식(foo)의 변수값 선언
        var result; // *Hoisting* var 변수값 선언
    
        foo = function() { // 함수표현식 할당
            return "hoisting";
        }
        result = foo();
        console.log("example of "+result);
    }
    func(); // "example of hoisting";
  • 함수표현식의 선언이 호출보다 아래에 있는 경우(var 변수) :: TypeError

    function func(testParam) { // 함수선언문
        console.log(foo); // "undefined" ... 선언은 되어 있지만 값 할당 x
        var result = foo(); // ERROR
        console.log("example of "+result);
    
        var foo = function(){ //함수표현식
            return "hoisting";
        }
    }
    func(); // TypeError: foo is not a function
    function func(testParam) { 
        var foo;
        console.log(foo); // "undefined" 
        //foo가 Hoisting에 의해 'undefined'로 지정되어 함수로 인식이 되지 않고 있다.
        var result = foo(); // ERROR
        console.log("example "+result);
    
        foo = function(){
            return "hoisting";
        }
    }
    func(); // TypeError...
  • 함수표현식의 선언이 호출보다 아래에 있는 경우(const/let 변수) :: ReferenceError

    function func(testParam) { // 함수선언문
        console.log(foo); // ERROR
        let result = foo();
        console.log("example of "+result);
    
        let foo = function() { //함수표현식
            return "hoisting";
        }
    }
    func(); // ReferenceError: foo is not defined

    let/const의 경우 그대로 읽어주면된다.

호이스팅 우선순위

같은 이름의 var 변수 선언과 함수 선언에서의 호이스팅

  • 변수 선언이 함수 선언보다 위로 끌어 올려진다.

    var name = "kim";
    
    function name(){ console.log("olive"); }
    function address(){ console.log("seoul"); }
    var address = "mapo";
    
    console.log(typeof name);
    console.log(typeof address);
    // *Hoisting* 변수값 선언
    var name;
    var address;
    
    // *Hoisting* 함수선언문
    function name(){ console.log("olive"); }
    function address(){ console.log("seoul"); }
    
    // 변수값 할당
    name = "kim";
    address = "mapo";
    
    console.log(typeof name); // "string"
    console.log(typeof address); // "string"
  • 값이 할당되어 있지 않은 변수와 값이 할당되어 있는 변수에서의 호이스팅

    var name = "kim"; // 값 할당
    var address; // 값 할당 x
    
    function name() { console.log("olive"); } // 같은 이름의 함수 선언
    function address() { console.log("mapo"); } // 같은 이름의 함수 선언
    
    console.log(typeof name); // "string"
    console.log(typeof address); // "function"
    • 값이 할당되어 있는 변수는, 변수가 함수선언문을 덮어쓴다.
    • 값이 할당되어 있지 않은 변수는, 함수선언문이 변수를 덮어쓴다.

[TIP] 호이스팅 사용 시 주의

  • 코드의 가독성과 유지보수를 위해 호이스팅이 일어나지 않도록 한다.
    • 호이스팅을 제대로 모르더라도 함수와 변수를 가급적 코드 상단부에서 선언하면, 호이스팅으로 인한 스코프 꼬임 현상은 방지할 수 있다.
    • let/const를 사용한다.

참고