코드 실행에 필요한 실행 컨텍스트를 통해 호이스팅, 스코프, 스코프 체인 이해하기!

실행컨텍스트란?


실행할 코드에 제공할 환경 정보들을 모아놓은 객체이며 **식별자(변수, 함수, 클래스 등의 이름)**를 등록하고 관리하는 스코프(렉시컬스코프에 환경레코드) 와 코드 실행 순서 관리를 구현한 내부 메커니즘이며 스택 형식으로 관리하는 것을 말합니다.

실행 컨텍스트 스택

const x = 1

function foo() {
  const y = 2

  function bar() {
    const z = 3
    console.log(x + y + z)
  }
  bar()
}

foo() // 6

위 코드를 실행할 경우 코드가 실행 순서에 따라 아래 사진과 같이 실행컨테스트 스택이 만들어지며 추가되고 삭제 됩니다. 이것을 실행 컨텍스트 스택이라고 하며 코드의 실행 순서를 관리합니다. 과정들을 하나하나 살펴보겠습니다.

1. 전역 실행 컨텍스트

코드를 실행하는 순간 전역 컨텍스트가 콜 스택에 추가되며 전역변수 const = x와 전역 함수 foo()가 전역 실행 컨텍스트에 등록되며 이후 전역 코드가 실행되기 시작하여 const = x에 값이 할당되고 함수 foo()가 호출된다.

2. foo 함수 실행 컨텍스트

foo() 함수가 호출되면 자바스크립트 엔진은 **foo()**에 대한 환경 정보를 수집해서 실행 컨텍스트를 생성한 후 콜 스택에 추가됩니다. 이때 foo() 함수의 지역변수 const = y와 함수 bar()가 실행 컨텍스트에 등록되며 foo() 함수가 실행되면 const = y에 값이 할당되고 bar가 호출된다.

3. bar 함수 실행 컨텍스트

bar() 함수가 호출되면 자바스크립트 엔진은 **bar()**에 대한 환경 정보를 수집해서 실행 컨텍스트를 생성한 후 콜 스택에 추가됩니다. 이때 bar() 함수의 지역변수 const = z 가 실행 컨텍스트에 등록되며 bar() 함수가 실행되면 const = y에 값이 할당되고 console.log가 호출된다.

실행 컨텍스트의 구조


실행 컨텍스트는 LexicalEnvironment(렉시컬 환경) 컴포넌트와 VariableEnvironment(변수 환경) 컴포넌트로 구성되어 있습니다. 이 둘 구성 모두 하나의 렉시컬 환경을 참조하지만 VariableEnvironment(변수 환경) 컴포넌트는 함수 실행 도중 변경사항이 있어도 즉시 반영되지 않고 초기 상태를 유지 합니다 이 부분을 제외하고는 모두 같기 때문에 구분하지 않고 LexicalEnvironmment(렉시컬 환경) 기준으로 설명하겠습니다.

LexicalEnvironmment

렉시컬 환경 안에는 2가지의 컴포넌트 구성이 되어 있습니다.

EnvironmentRecord(환경 레코드)

현재 컨텍스트와 관련된 코드들의 식별자 정보들이 저장되며 선언한 함수가 있을 경우 그 함수 자체와 var로 선언된 변수의 식별자 등을 말합니다. 이처럼 환경 레코드를 통해 변수 정보를 수집하는 과정을 모두마치게 되면, 코드가 실행되기 전임에도 불구하고 자바스크립트 엔진은 이미 해당 환경에 속한 코드의변수명들을 모두 알게 됩니다. 이때 등장하는 개념이 **호이스팅(hoisting)**인데 아래 코드를 보며 조금 더 자세히 알아보겠습니다.

식별자란? 변수나 함수의 이름이 될 수 있는 프로그래머가 선언하는 단어

// 환경 레코드를 통해 식별자 정보를 저장 하지 않을때 - 원본코드
function a() {
  var x = 1
  console.log(x)
  var x
  console.log(x)
  var x = 2
  console.log(x)
}

a()

// 환경 레코드를 통해 식별자 정보들이 저장되었을때 - 호이스팅을 마친 상태
function a() {
  var x
  var x
  var x

  x = 1
  console.log(x)
  console.log(x)
  x = 2
  console.log(x)
}

a()

실제로 자바스크립트 엔진이 끌어올리는 것은 아니지만 편의상 끌어올린 것으로 생각하며, 위 코드처럼 환경 레코드에 식별자들을 저장할 경우 모든 변수들이 최상단으로 끌어올린 다음 실제 코드를 실행하게 됩니다.


OuterLexicalEnvironmentReference (스코프체인)

let a = 1

function firstFun() {
  let b = 2
  console.log(a)

  function secondFun() {
    let c = 3
    console.log(b)
  }
}

위 코드를 보게 되면 전역에 선언되어 있는 let = a 변수는 어디에서든 접근이 가능하지만 firstFun() 함수 안에 있는 secondFun() 함수 안에 선언한 let = c 변수는 secondFun() 함수 안에서만 유효합니다. 이렇게 선언한 변수의 유효범위를 스코프라고 합니다. 그리고 유효범위를 안에서부터 바깥으로 차례로 검색해 나가는 것을 스코프 체인이라고 합니다. 그리고 이것을 가능하게 하는 것이 OuterLexicalEnvironmentReference 입니다.

OuterLexicalEnvironmentReference는 선언되었을 당시의 렉시컬 환경을 참조하여 상위 스코프를 결정합니다. 자신이 선언한 렉시컬 환경만 참조하고 있으며 가장 가까운 요소만 차례대로 접근할 수 있고 임의 순서대로 접근할 수는 없습니다. 그래서 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근이 가능합니다.



참고도서

  • 코어자바스크립트
  • 모던자바스크립트