JS - 실행 컨텍스트

Lee Ju-Hyeon(David)·2021년 9월 6일
0

Java Script

목록 보기
4/8
post-thumbnail

이 사이트를 번역하여 정리했습니다.

실행 컨텍스트란?

  • JS에서 scope, hoisting, this, function, closure 등의 동작 원리를 담고 있는 핵심.

1. 실행 컨텍스트의 종류

1.1 Global Execution Context

  • 전역 실행 컨텍스트
  • 기본적인 실행 컨텍스트를 의미한다
  • 함수 내부 코드가 아닌 모든 코드는 이에 해당한다
  • 전역 객체를 생성하고 this를 전역 객체에 바인딩한다
  • 단 하나만 존재할 수 있다

1.2 Functional Execution Context

  • 함수 실행 콘텍스트
  • 함수가 호출될 때마다, 새로운 실행 컨텍스트가 생성된다
  • 여러 개가 존재할 수 있다

1.3 Eval Function Execution Context

Eval 함수 내부 코드를 위한 컨텍스트로 일반적으로 사용하지 않는다.

2. Execution Stack

calling stack이라고 알려져 있는데 스택 구조로 이루어져 있다. JS에선 실행 컨텍스트는 스택 구조를 이용해서 관리된다.

let a = 'Hello World!';
function first() {
  console.log('Inside first function');
  second();
  console.log('Again inside first function');
}
function second() {
  console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');

  1. 전역 코드가 실행되면 전역 실행 컨텍스트가 생성되고 스택에 쌓인다.
  2. 함수를 호출하면 해당 함수의 컨텍스트가 생성되고 스택에 쌓인다.
  3. 함수가 종료되면 함수 실행 컨텍스트를 파기하고 이전 컨텍스트로 실행 컨트롤을 반환한다.

3. 실행 컨텍스트의 과정

실행 컨텍스트는 생성과 실행의 두 단계를 거친다.

3.1 실행 컨텍스트의 생성

생성 단계에서는 다음의 두 컴포턴트를 형성한다.

  • Lexical Environment(렉시컬 환경)
  • Variable Environment(변수 환경)
ExecutionContext = {
  LexicalEnvironment = <ref. to LexicalEnvironment in memory>,
  VariableEnvironment = <ref. to VariableEnvironment in  memory>,
}

3.1.1 렉시컬 환경

렉시컬 환경이란 ECMAScript 코드lexical nesting structure에 기반한 특정한 변수와 함수의 연결을 정의하는 구조다.

쉽게 말해서 식별자와 변수의 맵핑을 담당한다고 할 수 있다.

var a = 20;
var b = 40;
function foo() {
  console.log('bar');
}

위의 코드를 예시로 렉시컬 환경을 아래와 같이 표현할 수 있다.

lexicalEnvironment = {
  a: 20,
  b: 40,
  foo: <ref. to foo function>
}

렉시컬 환경은 다음 세 가지 컴포넌트를 가진다

Environment Record
렉시컬 환경에서 변수와 함수의 선언을 저장하는 공간이다. ER은 두 가지 타입이 있다.

  • Declarative environment record
    변수와 함수의 선언을 저장하는 공간이다. 함수 코드에 대한 렉시컬 환경은 이를 포함한다.
  • Object environment record
    전역 코드에 대한 렉시컬 환경은 이를 포함한다. 변수와 함수의 선엔에 대한 것을 제외하고, Object ER은 전역 객체 또한 포함한다.

참고로, ER은 arguments라는 객체를 포함하는데 아래와 같다.

function foo(a, b) {
  var c = a + b;
}
foo(2, 3);

// argument object
Arguments: {0: 2, 1: 3, length: 2},

Outer Environment Reference
ES3에서 공식적으로 사용되던 용어는 Scope Chain이며, ES5부터는 용어가 바뀌었다.

함수가 중첩 상태일 때 하위함수부터 상위함수로 탐색을 한다. 만약에 전역 실행 컨텍스트에서도 탐색에 실패하면 참조 오류가 발생한다.

출처 : https://poiemaweb.com/js-execution-context


This Binding
이전 포스팅에서 다루었다

3.1.2 변수 환경

ES6에서는 렉시컬 환경과 변수환경의 차이점은 하나이다. 렉시컬 환경은 함수와 let/const 변수 바인딩에 관한 정보를 저장하는 반면, 변수 환경은 var 변수 바인딩에 관련된 정보만 저장한다.


3.2 실행 컨텍스트의 실행

컨텍스트 생성 단계에서 모든 변수에 대한 할당이 끝나면 실행 단계로 넘어간다.

아래의 예제를 따라가자.

let a = 20;
const b = 30;
var c;
function multiply(e, f) {
 var g = 20;
 return e * f * g;
}
c = multiply(20, 30);

위의 코드가 실행되면, JS 엔진은 전역 코드를 실행하기 위한 전역 실행 컨텍스트를 생성한다. 생성된 컨텍스트는 아래와 같다.

GlobalExectionContext = {
  // 렉시컬 환경
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 전역 변수인 a, b 저장
      // 전역 함수인 multiply 저장
      a: < uninitialized >,
      b: < uninitialized >,
      multiply: < func >
    }
    outer: <null>,
    ThisBinding: <Global Object>
  },
  VariableEnvironment: {
    // 변수 환경
    EnvironmentRecord: {
      Type: "Object",
      // var 형 변수인  c는 변수 환경에 저장된다.
      c: undefined,
    }
    outer: <null>,
    ThisBinding: <Global Object>
  }
}
// let과 const로 선언된 변수는 초기화되지 않은 상태로 할당되고,
//var로 선언된 변수는 기본값(undefined)가 할당되어 있다.
//이는 호이스팅과 관련이 되어 있다.
//이러한 동작 이유로 var은 선언되기 전에 참조할 수 있지만,
//let과 const는 불가능하다(참조 에러)

실행 단계로 넘어가면, 모든 변수에 대한 할당이 완료되어 있다.

GlobalExectionContext = {
LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 전역 변수 값이 할당된다.
      a: 20,
      b: 30,
      multiply: < func >
    }
    outer: <null>,
    ThisBinding: <Global Object>
  },
VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      c: undefined,
    }
    outer: <null>,
    ThisBinding: <Global Object>
  }
}

이제 multiply함수를 마주치게 되면, 새로운 함수 실행 컨텍스트를 생성한 뒤에, 스택에 쌓는다.

FunctionExectionContext = {
LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // argument 객체에 저장된다.
      Arguments: {0: 20, 1: 30, length: 2},
    },
  	//상위 컨텍스트를 가리키는 outer
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  },
VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // Identifier bindings go here
      g: undefined
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>
  }
}

이후에 실행 단계로 넘어간다.(함수 실행 컨텍스트의 변수 할당이 모두 완료됨)

여기서 잠깐 헷갈릴 수 있는 부분은 위에서 이미 실행단계로 넘어갔다고 생각할 수 있다는 것이다. 모든 실행 컨텍스트는 똑같이 생성과 실행 단계가 있다. 따라서 앞서 전역 실행 컨텍스트도 실행단계가 있고, 현재 함수 실행 컨텍스트에서도 실행단계가 있다.

FunctionExectionContext = {
LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      Arguments: {0: 20, 1: 30, length: 2},
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>,
  },
VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      g: 20
    },
    outer: <GlobalLexicalEnvironment>,
    ThisBinding: <Global Object or undefined>
  }
}

함수 실행이 완료되면, return값이 c에 저장된다. 그리고 전역 렉시컬 환경에 업데이트 하게 된다. 전역 코드가 완료되면 프로그램이 종료된다.

0개의 댓글