실행컨텍스트 (2)-관리와 생성

23200 단어 JavaScriptJavaScript

실행컨텍스트는 자바스크립트의 실행에 근본이 되는 핵심적인 내용이다.

다음 3가지의 목차로 실행컨텍스트를 이해 하도록 정리한다.

실행컨텍스트 (1)-개요와 구성 👈

실행컨텍스트 (2)-관리와 생성 👈

실행컨텍스트 (3)-식별자 찾기 + 클로저 👈

앞선 포스팅에서 실행컨텍스트의 구성요소에 대해 알아 보았지만,

정작 실행컨텍스트가 어떻게 생성되고 어떻게 실행되는지 알 수 없었다.

이번 포스팅에서 실행컨텍스트의 생성과 실행을 주제로 알아본다.

🧩 실행컨텍스트의 관리

프로그램 실행에 있어 실행컨텍스트는 Stack 이라는 자료구조로 관리된다.

그리고 실행컨텍스트가 push 되고 pop 되는 stack을 호출 스택, Call stack 이라고 부른다.

stack 자료구조의 특성상 가장 위에있는 값이 늦게 들어온 값이며(LIFO)

콜 스택에서는 가장 위에 있는 실행컨텍스트가 현재 실행 중인 실행컨텍스트라고 볼 수 있다.

다음 예시를 통해 Call stack을 자세히 확인한다.

var thisIsInGlobal = "hello"

function f1(){
  console.log("first")
  function f2(){
    console.log("second")
  }
  f2()
}
f1()
console.log(thisIsInGlobal)

1 : global Execution Context(EC) push

2 : 함수의 선언이 아닌 호출 f1의 EC push

3 : console.log "first" EC push

4 : console.log "first" EC pop ⇒ 실제 콘솔이 찍힌다.

5 : f2 EC push

6 : console.log "second" EC push

7 : console.log "second" EC pop

8 : f2 EC pop

9 : f1 EC pop

10 : console.log "hello" EC push

11 : console.log "hello" EC pop

12 : global EC pop

이를 통해 알 수 있는 것은

  1. 프로그램이 시작되면 가장 먼저 Global EC 를 생성한다.
  2. 함수가 호출되면 그 함수의 실행컨텍스트가 생성 되고 콜스택에 push 된다.
  3. 콜스택의 EC가 pop이 되면 실행된다.

🧩 실행컨텍스트의 생성과 실행

실행컨텍스트는 두가지 단계를 거치며 생성과 실행을 한다.

1️⃣ Creation phase

1️⃣ -1️⃣ LexicalEnvironment 컴포넌트를 생성한다. → let, const 변수, 함수 선언식 매핑

1️⃣ -2️⃣ VariableEnvironment 컴포넌트를 생성한다. → var 변수 매핑

⚡️ 함수 실행컨텍스트의 경우 EnvironmentRecord 에 arguments 와 length를 받음

2️⃣ Execution phase

변수를 할당한다.

다음과 같은 예시를 통해 실행컨텍스트의 자세한 생성과 실행을 확인 한다.

var a = 1
let b = 2

function f1(x, y){
  var c = 3
  let d = 4
  return x+y
}

let answer = f1(100, 200)
  1. 글로벌 실행컨텍스트 Creation phase
GlobalExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      b: < uninitialized >,              // let 변수인 b는 메모리에 매핑, 그러나 아직 초기값이 없는 상태로 할당 X
      f1: < func >,                      // 함수 선언식 f1 매핑
      answer: < uninitialized >          // let 변수인 함수 결과값 answer 매핑 (초기값 X)
    }
    outer: <null>,
    ThisBinding: <Global Object>
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      a: undefined,                      // var 변수인 a 는 undefined 으로 초기값을 할당
    }
    outer: <null>,
    ThisBinding: <Global Object>
  }
}
  1. 글로벌 실행컨텍스트 Execution phase
GlobalExectionContext = {
LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      b: 2,                                // execution phase 에서 변수의 값이 할당 
      f1: < func >,
      answer: < uninitialized >
    }
    outer: <null>,
    ThisBinding: <Global Object>
  },
VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      a: 1,                                // execution phase 에서 변수의 값이 할당 
    }
    outer: <null>,
    ThisBinding: <Global Object>
  }
}
  1. 함수 실행컨텍스트 Creation phase
FunctionExectionContext = {
LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      Arguments: {0: 100, 1: 200, length: 2},       // 함수는 arguments를 0부터 순서대로 받음
      d: < uninitialized >                          // let 변수인 d는 초기값이 할당되지 않음
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  },
VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      c: undefined                                  // functional 스코프를 따르는 c 는 undefined로 초기화
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>
  }
}
  1. 함수 실행컨텍스트 Execution phase
FunctionExectionContext = {
LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      Arguments: {0: 100, 1: 200, length: 2},
      d: 4                                           // execution phase 시점에서 값이 할당된다.
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  },
VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      c: 3                                           // execution phase 시점에서 값이 할당된다.
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>
  }
}

🧩 실행컨텍스트와 호이스팅

호이스팅?

호이스팅은 자바스크립트에서 변수나 함수의 선언을 최상단으로 끌어올려 선언하는 것 이라고 한다.

console.log(hoisting1) // undefined
console.log(hoisting2) // ref error !
var hoisting1 = 1
let hoisting2 = 2

위의 예시에서 보면 var 변수로 선언한 hoisting1 의 경우 선언되기 전에 호출했기 때문에 에러가 나는것이 당연?할 것 같지만,

undefined 라는 '값'으로 출력되며 에러를 발생시키지 않는다.

반면, hositing2 의 경우 에러를 발생시키는 것을 알 수 있다.

사실, 코드상으로 보면 자바스크립트 엔진이 코드를 해석할때 var 변수를 최상단으로 올리고 해석한 것으로 보이지만

실제로 호이스팅의 개념은 실행컨텍스트의 개념이 적용되어 있다.

위의 예시 코드를 실행컨텍스트에서 확인한다면,

/* Creation Phase */
GlobalExectionContext = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      hoisting2: < uninitialized >       // let 변수의 경우 lexicalEnvironment에서 초기값 할당 X         
    }
    outer: <null>,
    ThisBinding: <Global Object>
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      hoisting1: undefined,             // var 변수의 경우 undefined로 초기값 할당 - hoisting이 일어나는 부분
    }
    outer: <null>,
    ThisBinding: <Global Object>
  }
}

/* Execution Phase */

변수들의 값 할당
... 생략 

실행컨텍스트가 생성 단계, 실행 단계를 거치는데

var 변수의 경우 생성 단계에서 이미 undefined라는 값으로 할당 되어 있기 때문에 생성 시점 이후(실행 전) 에서도 에러를 발생시키지 않는다.

let 변수는 초기값이 생성 단계에서 할당되지 않기 때문에 실행 전에 변수를 호출하면, 참조에러를 발생시킨다.

호이스팅은 변수 혹은 함수 즉, 선언과 실행 사이에서 발생하는 자바스크립트의 독특한? 실행 원리라고 볼 수 있다.

References


모던 자바스크립트 입문

Call Stack과 Execution Context 를 알아보자

Understanding Execution Context and Execution Stack in Javascript

좋은 웹페이지 즐겨찾기