JS

[JavaScript] 호이스팅, 스코프

Junly_21 2023. 9. 10. 22:06

스코프를 알기 위해서는 호이스팅을 알아야한다.

또한 호이스팅을 알기 전 실행컨텍스트의 개념을 알고, 실행컨텍스트 객체 안에 담긴 environmentRecord를 알아야한다. 실행컨텍스트

 

[JavaScript] 실행 컨텍스트

코어 자바스크립트 2강 - 실행컨텍스트 실행 컨텍스트 실행할 코드에 제공할 환경 정보들을 모아놓은 객체 스코프와 클로저 라는 개념을 이해하기 전에 살펴보자. 우리가 다음과 같은 JS파일을

junly21.tistory.com

 

호이스팅

environmentRecord에는 현재 컨텍스트에 관한 정보들이 저장된다. 이 때 컨텍스트 내부에 함수 선언문이 있다면 '함수 그 자체'를 수집하고, var로 선언된 변수도 수집하고 한다. 

 

이렇게 변수 정보 수집을 마쳐도 실행 컨텍스트가 관여할 코드들은 실행되기 전의 상태이다. 코드가 실행되기 전에 우리는 그 내부의 변수명, 선언된 함수 자체 등을 알고 있게 되는것이다. 그래서 끌어올림의 개념인 호이스팅 이라는 개념이 등장했다.

 

아래의 코드 동작 결과를 예상해보자

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

뭔가 1, undefined, 2 순으로 출력이 될 것 같이 생겼다. 

그러나 호이스팅을 고려해 동작을 예상해보면 

function a () {
    var x; //a의 파라미터도 여기서 선언
    var x;
    var x;
    console.log(x); //(1)
    console.log(x); //(2)
    x = 2;
    console.log(x); //(3)
}
a(1)

이렇게 선언이 위로 올라가서 1 1 2가 출력되게된다.

 

그럼 아래 코드는 어떻게 될까?

function a() {
  console.log(b);
  var b = "bbb";
  console.log(b);
  function b() {}
  console.log(b);
}
a();

위의 예제를 참고하면 undefined, bbb, function b()가 출력될 것 같은데...

하지만 맨 위의 설명을 보면 선언된 함수(위 코드에서는 function b() )는 그 자체를 가져온다고 했다.

그렇기 때문에 동작 흐름은 아래와 같다.

function a() {
  var b;
  function b() {}
  console.log(b);
  b = "bbb";
  console.log(b);
  console.log(b);
}
a();

따라서 결과는 function b(), bbb, bbb순으로 출력된다.

 

참고

함수를 정의하는 세 가지 방식

 

function a(){ //함수 선언문. a가 함수명

}

var b = function(){ //b에 익명 함수를 할당하였음. 익명함수 표현식.

}

var c =  function d(){ //c에 d라는 함수를 할당. 기명 함수 표현식.

}

함수 선언문은 함수 전체를 호이스팅해서 끌어올리는게 된다.

그러나 함수 표현식은(기명, 익명 둘 다) 선언만 올라가고 함수를 변수에 할당하는 부분은 아래 냅둔다. 즉 함수 자체가 호이스팅이 되지는 않는다. 

이 차이는 혼란의 원인이 되기는 한다만 에러의 발생 원인이 될 수도 있으니 알고 있도록 하자.

 

스코프

스코프란?  - 식별자의 유효범위.

 

스코프 체인

우리가 실행 컨텍스트 글에서 배웠던outerEnvironmentReference는 현재 호출된 함수가 '선언될 당시'의 LexicalEnvironment를 참조한다. 기억이 안나면 보고오자.

 

A함수 안에 B함수, B함수 안에 C함수가 있다고 할 때, 

1.C의 outerEnvironmentReference는 B의 LexicalEnvironment를 참조한다.

2.B outerEnvironmentReference는 A의 LexicalEnvironment를 참조한다.

2.A outerEnvironmentReference는 전역컨텍스트의 LexicalEnvironment를 참조한다.

 

위의 원리에 의해

var a = 1;
function outer() {
  var inner = function () {
    console.log(a); //첫번째로 1이 찍힌다
  };
  inner();
  console.log(a);
}
outer();
console.log(a);

이 코드를 실행할 시 outer함수를 실행하고, outer함수 내부의 inner함수를 실행하고, inner 내부의 console.log(a)를 찍어도 계속 outerEnvironmentReference를 참조해 1이 나오게 된다. 이를 스코프체인이라고 부른다.

 

그러나

var a = 1;
function outer() {
  var inner = function () {
    console.log(a); //첫번째로 1이 찍혔었는데...
    var a; //여기가 추가되면??
  };
  inner();
  console.log(a);
}
outer();
console.log(a);

이 코드는 inner 함수의 LexicalEnvironment에 a가 '값이 할당되지 않은 채로' 선언 되어있으므로
undefined, 1, 1이 출력되게 된다!!