ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 일찍 알았으면 좋았을 호이스팅
    일찍 알았으면 좋았을 넓고 얕은 개발 지식 2024. 10. 27. 22:01

    자바스크립트의 동작 원리를 공부하다보면 참 이해가 될듯 안될듯 한 개념이 호이스팅입니다.

    호이스팅 개념을 정리하면서 어떤 개념인지 좀더 자세히 알아보겠습니다.

    1. 호이스팅(Hoisting)이란?

    MDN에서는 호이스팅을 이렇게 설명하고 있어요.

    인터프리터가 코드를 실행하기 전에 함수, 변수, 클래스 또는 import의 선언문을 해당 범위(스코프)의 맨 위로 끌어올리는 것처럼 보이는 현상 입니다.

    여기서 인터프리터는 소스 코드를 읽어서 프로그램을 실행시키기 위한 방법인데, 한줄 한줄 읽어가면서 즉시 실행시키는 방식입니다. 여기서 인터프리터가 코드를 실행하기 전 이라는 표현을보고 사전에 뭔가 실행되는 단계가 또 있는것 같다고 생각했습니다. 우선은 이런 단계가있다는것만 알아두고 뒤에서 자세하게 다루겠습니다. 우선은 호이스팅이 어떤 동작을 말 하는건지 보겠습니다.

    2. 호이스팅으로 여겨질 수 있는 타입은

    다음 행위 중 하나라도 호이스팅으로 간주될 수 있습니다.
    1.변수가 선언된 줄 이전에 해당 범위에서 변수 값을 사용할 수 있는 경우 ("값 호이스팅")
    2.변수가 선언된 줄 이전에 해당 범위의 변수를 참조할 수 있지만 ReferenceError를 던지지 않고 값이 항상 undefined인 경우입니다. ("선언 호이스팅")
    3.변수를 선언하면 변수가 선언된 줄 앞의 스코프에서 동작이 변경됩니다.
    4.선언의 부작용은 선언이 포함된 나머지 코드를 평가하기 전에 발생합니다.

    글로만 봐선 어떤 유형인지 잘 이해가 안가서 코드로 보겠습니다.

    1. 변수가 선언된 줄 이전에 해당 범위에서 변수 값을 사용할 수 있는 경우

    console.log(myFunction()) // hello World! 정상작동  
    
    function myFunction(){  
      return "hello world!"  
    }  
    
    console.log(myFunction()) // hello World! 정상작동  

    함수 선언은 선언 이전에 함수를 사용할 수 있는 유형1 호이스팅이 적용됩니다. 함수 선언문이 스코프의 맨 위로 값과 함께 호이스팅 됩니다. 즉, 함수 선언은 코드의 어디에 위치하든 선언된 줄 이전부터 정의된 것처럼 호출이 가능합니다.

    2. 변수가 선언된 줄 이전에 해당 범위의 변수를 참조할 수 있지만 ReferenceError를 던지지 않고 값이 항상 undefined인 경우

    console.log(myVar); // undefined   
    var myVar = 5;  
    console.log(myVar) // 5  

    변수의 호이스팅은 선언과 할당을 한줄에 적어도, 선언부만 끌어올려지고, 할당부는 자리에 남아있어서 var로 선언한 변수는 다음과 같이 해석됩니다.

    var myVar  
    console.log(myVar) //undefined  
    
    myVar = 5  
    console.log(myVar) //5  

    그래서 var로 선언한 변수보다 윗 줄에서 선언한 변수의 값을 참조해도 undefined로만 나오는 이유 입니다.

    3. 변수를 선언하면 변수가 선언된 줄 앞의 스코프에서 동작이 변경됩니다.

    여기서 동작이 변경된다는건 해당 스코프 내에서 코드의 동작 방식에 영향을 준다는 의미 입니다. 호이스팅이 일어나면 코드의 실행 순서와 변수의 유효 범위에 영향을 주기때문에 호이스팅을 이해하면 변수 선언이 코드 전체에 어떻게 영향을 미치는지 파악할 수 있습니다.

    아래는 호이스팅으로인한 변수 섀도잉으로 대표적인 실행 순서와 변수의 유효범위가 변화되는 예시 입니다.,

    var a = 10;  
    
    function test() {  
      console.log(a); // undefined  
      var a = 20;  
      console.log(a); // 20  
    }  
    
    test();  

    선언된 변수의 위치로만 보면 함수안에서 호출하는 console.log a는 전역변수 var a를 참조 할 것 같습니다. 그렇지만 test함수가 호출되고 test함수 스코프가 생성되면서 호이스팅이 되면 var a 선언부가 test함수 스코프 최상단으로 끌어올려지고, 초기화된 값 undefined를 console.log가 참조해 마치 전역변수가 가려지는 것처럼 됩니다.

    다른 예로는 변수 키워드 중 let, const로 선언할 때 TDZ의 영향으로 선언한 줄 보다 위에서 접근하면 Reference Error로 변수 참조가 안되는 현상도 동작이 변경되는 예시 중 하나 입니다.

    console.log(myLet); // ReferenceError 발생 이 아래로는 실행 중지  
    
    let myLet = 10;  
    

    4. 선언의 부작용은 선언이 포함된 나머지 코드를 평가하기 전에 발생합니다.

    이 타입은 특히 import 선언일때 대표적입니다. import 선언은 모듈을 로드하면서 그 모듈의 모든 코드가 실행되기 때문에 sideEffect(부작용)이 발생할 수 있습니다.

    //module.js 파일  
    console.log('module.js가 로드되었습니다.');   
    export const data = 42;  
    //main.js 파일  
    console.log('main.js 시작');   
    
    import { data } from './module.js';   
    
    console.log('main.js의 data:', data);  

    main.js에서 module.js를 import하면 import문보다 윗 줄에 코드가 있더라도 코드가 물리적으로 코드 위치가 바뀌진않지만 import문이 호이스팅 되어서 무조건 먼저 실행됩니다. 그리고 나머지 코드가 실행됩니다. 그래서 console.log는 아래와 같이 실행됩니다.

    console.log('module.js가 로드되었습니다.');   
    console.log('main.js 시작');   
    console.log('main.js의 data:', data); // 42  
    

    3. 인터프리터가 코드를 실행하기 전 단계는?

    브라우저의 자바스크립트 엔진이 코드를 한 줄 한 줄 읽어내려가면서 실행하기 전에, 코드를 읽어 구문을 이해하는 파싱 단계와, 컴파일 단계가 있고, 실행컨텍스트를 생성하는 단계가 있습니다. 호이스팅은 물리적으로 코드의 선언문을 이동시키는게 아니라, 엔진의 처리 과정에서 선언들을 미리 등록해 실행 단계에서 사용할 수 있게 하는 것 입니다.
    그래서 MDN의 표현을 빌려 인터프리터가 코드를 실행하기 전이라 함은,

    인터프리터가 코드를 실행하기 전( === 실행 컨텍스트의 생성 단계에서) 에 함수, 변수, 클래스 또는 import의 선언문을 해당 범위(스코프)의 맨 위로 끌어올리는 것처럼 보이는 현상 입니다.

    이렇게 이해할 수 있겠습니다.

    4. 마무리

    그동안, 단순히 스코프의 최상단으로 끌어올려진다 라고만 단순하게 생각했던 호이스팅을 예시코드, MDN의 정의와 표현을 찾아보면서 어떻게 동작하는지 정리해보니 모호했던게 하나둘 씩 자리를 잡는 것 같습니다. 책으로만 보던 개념들을 손수 글로 정리하니 이해와 정리되는 효과가 더 좋은것같네요.

Designed by Tistory.