본문 바로가기

개발/연구

[js 기본] 호이스팅과 변수의 생성 과정

자바스크립트의 변수는 var, let, const가 있고, 각자의 특징이 있습니다.

 

변수 호이스팅

function 호이스팅(){
  var var_ = "I am var"
  
  console.log(var_)
}

호이스팅()

// output : I am var

그리고 공통점이 있는데요. 위와 같이 변수를 생성하고 나서 참조할 수 있다는 점입니다. 과연 그럴까여?

 

function 호이스팅(){
  console.log(var_)
  console.log(var_2)
  var var_ = "I am var"
  
}

호이스팅()

// output : 
//	undefined
//	var_2 is not defined	

console.log출력 이후에 나오는 var_는 호출할 수 있었습니다(물론, undefined이지만요.). 그렇지만 아예 선언하지 않은 var_2의 경우는 정의되지 않았다고 나오네요. 

 

사실 모든 변수와 함수는 자바스크립트가 실행 컨텍스트로 올려질 때, 최상단에 재배정됩니다. 이를 호이스팅(hosting:끌어올림)이라고 합니다. 그렇다면 var 말고 다른 변수도 봅시다.

 

function let호이스팅(){
  console.log(let_)
  let let_ = "I am let"
}

// output : Cannot access 'let_' before initialization

let의 경우는 되지 않습니다. const도 마찬가지인데요. 왜 이럴까요? 호이스팅이 되지 않는걸까요?

사실 그렇지 않습니다. 유효 범위에 따라 끌어 올려지는 위치가 달라지기 때문인데요. 다음을 봅시다.

 

블록 레벨 스코프 / 함수 레벨 스코프의 호이스팅

function scope_hoisting(){
  console.log(var_)
  console.log(let_)
  {
    let let_ = "I am let"
  }

  {
    var var_ = "I am var"
  }
}

scope_hoisting()

// 	output :
//	undefined
//	ReferenceError: let_ is not defined

var_는 호출할 수 있었지만, let_의 경우 선언되지 않은 변수라고 나오는군요. 이것은 유효 범위와 연관이 있답니다. 호이스팅이 되는 위치는 유효 범위 중 최상단에 비치되기 때문인데요. 하단의 코드는 호이스팅이 된 코드입니다.

 

function scope_hoisting(){
  var var_
  console.log(var_)
  console.log(let_)
  {
    let let_ // 정확하게는 초기화가 되지 않은 상태입니다. 이해를 돕기 위해 이 위치에 비치하였으니 참고만 해주시고, 하단 변수 생성 단계를 참고해주세요.
    let let_ = "I am let"
  }

  {
    var var_ = "I am var"
  }
}

scope_hoisting()

그렇다면 왜 undefined이 뜨는지 유추가 가능할 것 같은데요. 조금 더 자세히 알아봅시다.

자바스크립트에서는 3 단계를 통하여 변수가 생성됩니다.

 

변수 생성 단계

1. 선언

선언단계에서는 변수 객체를 실행 컨텍스트에 '등록' 합니다. 이 후에 해당 스코프의 변수를 참조하게 됩니다.

2. 초기화

등록된 변수의 메모리를 확보합니다. 변수에 undefined로 초기화 해줍니다.

3. 할당

실질적으로 할당할 값을 변수에 대입해줍니다.

 

여기서 var의 경우 선언과 초기화를 한번에 수행합니다. 그렇기에 undefined를 출력하게 되었지요. 그렇지만 let의 경우 선언 단계와 초기화 단계를 따로 수행하게 됩니다. 그렇기에 일시적으로 ReferenceError가 발생하게 되는 구간이 생기게 되는 것(TDZ: 일시적 사각지대)이지요. 에러를 일찍이 발견할 수 있기에 블록 레벨 스코프의 변수들은 더욱 안정적인 개발을 가능하게 돕습니다. 그렇기에 let과 const를 변수를 사용하도록 권장하지요.

 

함수 호이스팅

함수 호이스팅은 변수 호이스팅과 조금 다른 방식을 가져요. 물론 기본적인 원리는 같아요. 그렇지만 변수 생성 단계와 조금 차이가 있답니다. 일반적인 함수 선언문을 사용한 호이스팅을 살펴보도록 해요.

// 함수 호이스팅 - 함수 선언문
var res = square(5);

function square(number) {
  return number * number;
}

조금 다른 특징이 느껴지나요? 변수 호이스팅의 var와 대입해보자면, undefined값을 가져야 할 것입니다. 그렇지만 실행이 잘 되는 것을 볼 수 있죠. 이 점이 변수 생성 단계와 다른 점이예요. var의 경우는 선언과 초기화가 동시에 일어났지요? 함수는 선언, 초기화, 할당이 한번에 일어난답니다. 그렇기에 바로 할당할 수 있죠. 근데요, 함수 표현식에서는 조금 달라져요.

// 함수 호이스팅 - 함수 표현식
var res = square(5); // TypeError: square is not a function

var square = function(number) {
  return number * number;
}

// square는 undefined

함수 표현식변수에 함수 표현식(익명 또는 기명)의 주소를 전달하는 방법입니다. 그런데 not a function인 것을 볼 수 있지요. 그건 square가 변수로 취급되기 때문이예요. square가 var로 선언되어 있으니 undefined가 되는거지요. 그러니, 함수 표현식에서는 함수 호이스팅이 아닌, 변수 호이스팅으로 처리된다고 보면 쉬워요.

 

추가적으로 함수 표현식 vs 함수 선언문 둘 중 어떤게 좋을까요? 많은 사람들은 함수 표현식 사용을 권고합니다.

그 이유는 인터프리터가 함수를 너무 많은 변수 객체에 저장하여 성능 상 문제가 생길 수 있어요. 또한 함수 표현식은 클로저로서 활용할 수도 있고, 콜백 인자로 사용이 가능하니, 많은 장점이 있어요.

ref :
https://poiemaweb.com/js-function
poiemaweb.com/es6-block-scope