[코어 자바스크립트 뽀개기] 실행 컨텍스트(execution context)

박한준·2021년 2월 21일
1

Front-End-dev

목록 보기
8/8

Javascript에서 코드를 실행할 때 어떠한 과정을 거치는 지에 대해 깊이 고민하지 못했었습니다. 코드는 기본적으로 위에서 아래로 실행되고 스코프(Scope)를 기준으로 영향력이 제한된다는 정도만 알고 나머지는 실행의 결과만 신경썼습니다. "코어 자바스크립트"의 이번 장은 실행 컨텍스트(execution context)를 통해 코드가 어떠한 환경에서 실행되는지와 코드 실행의 원리를 알 수 있었습니다.

실행 컨텍스트(Execution Context)

실행 컨텍스트는 실행할 코드에 제공되는 환경 정보를 모아놓은 객체입니다. 따라서 동일한 환경정보를 공유하는 코드들은 하나의 실행 컨텍스트를 가집니다. 그렇다면 "동일한 환경"은 어떤 것을 기준으로 판단하는 것일까요?

  • 전역변수(global)
  • eval() 함수
  • 함수(function)

여기서 전역변수는 자동으로 실행되고, eval()함수는 악마(?)로 취급받습니다.(참고) 따라서 우리가 의도적으로 실행 컨텍스트를 생성, 관리할 수 있는 것은 함수가 거의 유일합니다.

Javascript 코드를 실행하기 전 일어나는 일

Javascript 엔진이 처음 Script를 만나면 Global Object를 생성합니다. Global Object 안에는 내장 함수를 포함한 빌트인 객체와 BOM DOM 등이 포함되어 있다.

그 다음 Global 실행 컨텍스트를 생성하고 콜 스택(Call Stack)에 Global Object를 저장합니다. Call Stack은 코드를 실행하는 도중 실행 컨텍스트들을 저장하는 스토어(Store) 역할을 합니다.

LIFO(Last in, First out) structure로 이루어져 있어서 가장 위에 올려진 실행 컨텍스트가 모두 실행이 완료되면 제거되는 형태로 전체 코드를 실행합니다.(일반적인 스택 구조)

함수를 실행하면 벌어지는 일

함수를 실행하는 순간 Javascript 엔진은 함수 실행 컨텍스트를 생성하고 콜 스택에 쌓아 놓습니다.
함수 실행 컨텍스트를 생성할 때 다음과 같은 과정을 거칩니다.

Creation Phase

이 단계에서는 LexicalEnvironment와 VariableEnvironment를 생성합니다.

Lexical Environment
공식 문서에서의 정의

A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code. A Lexical Environment consists of an Environment Record and a possibly null reference to an outer Lexical Environment. Usually a Lexical Environment is associated with some specific syntactic structure of ECMAScript code such as a FunctionDeclaration, a BlockStatement, or a Catch clause of a TryStatement and a new Lexical Environment is created each time such code is evaluated.

간단히 말하면 실행 컨텍스트 내에서 특정 변수 및 함수에 대한 식별자 연결을 정의하는 데 사용되는 규격(객체)라고 볼 수 있습니다. 따라서 Lexical Environment는 변수에 접근하기 위해 필요한 환경으로 구성되어 있습니다. Lexical Environment를 구성하는 요소를 간단히 살펴보겠습니다.

  1. Environment Record : 변수와 함수 선언이 저장되는 곳입니다.
  2. Reference to the Outer Environment : 외부 환경에 대한 참조입니다. 이를 활용하여 scope chain이 이루어진다고 볼 수 있습니다. closure가 가능한 환경을 만들기도 합니다.
  3. this binding: 현재 바라보는 this 객체를 담고 있습니다.

쉽게 말을 풀어보면 식별자-변수 매핑을 보유하는 구조입니다. 각 실행 컨텍스트 내부의 변수들을 수집하여 모아 놓은 객체라고 생각하면 좋을 것 같습니다.

Variable Environment
선언 시점의 Lexical Environment의 스냅샷입니다. 따라서 구성은 Lexical Environment와 같고, 최초 실행에서는 Lexical Environment와 동일합니다. 코드 진행에 따라서 변경되지 않고 선언시점의 상태를 그대로 유지하는 것이 특징입니다.

또한 ES6에서 Lexical Environment 구성 요소와 Variable Environment 구성 요소 사이의 한 가지 차이점은 전자는 함수 선언과 변수(let and const) 바인딩을 저장하는 데 사용되는 반면 후자는 변수(var) 바인딩만 저장하는 데 사용된다는 것입니다.

Execution Phase

이 단계에서 코드를 실행하면서 이 전에 긁어 모았던 변수들에 값을 할당합니다.

호이스팅과 연결지어보자

호이스팅(hoisting)과 관련해서는 코드를 실행하기 전에 변수들을 모아 위에 끌어올려 미리 정의하는 것으로만 관념적으로 알고 있었습니다. 하지만 위에서 말한 것과 같이 실행컨텍스트의 작동 원리를 알게 되면서 호이스팅이 왜 일어나게 되었는지 이해할 수 있었습니다.

Javascript 엔진은 실행 컨텍스트를 생성할 때 미리 변수에 대한 정보들을 끌어 모아서 환경 구성을 합니다. 여기서 값을 배정하지 않고 말 그대로 "환경"만 구성합니다. 이번에 실행할 환경에서 어떠한 변수들이 있는지 미리 탐색한다는 의미로 볼 수 있습니다.

이 때 함수 선언 방식에 따라 미리 변수를 컨텍스트에 담는 방식이 달라지는데, es6의 let 과 const는 Creation Phase에서 값을 가지고 있지 않고, var로 정의한 변수는 undefined를 배정합니다. 이 때문에 let, const는 hoisting이 되지 않고 "reference error"를 발생시키는 것입니다.

클로저와 연결지어보자

클로저(closure)는 함수의 "선언시점"에 접근가능한 변수를 다른 환경에서도 접근이 가능하게 되는 현상을 말합니다. 이 개념도 실행 컨텍스트의 동작방식으로 이해할 수 있었습니다. 실행 컨텍스트는 모두 외부 환경을 참조하고 있습니다. 그리고 이 모든 환경 객체는 "선언시점"에 생성됩니다. 따라서 선언시점의 환경에 접근할 수 있는 것입니다.

선언시점의 환경을 참조한다는 의미는 외부환경 또한 참조를 당한(?)다는 의미입니다. 따라서 외부환경의 실행컨텍스트는 이미 종료가 되어 Garbage Colletor에게 수거당할 위기에 처해있지만 외부환경으로써 참조되어 있으니 GC의 수거 대상에서 제외됩니다. 1개라도 참조 카운트가 있다면 GC가 수거하지 않으니까요.

느낀점

Javascript의 특징들(closure나 hoisting)에 대한 개념을 그냥 경험과 이론적으로만 알고 있었습니다. 그 개념들의 결과만 알고 있었지, 그 원인에 대해서는 생각하지 못했던 것 같습니다. 실행 컨텍스트 관련 부분을 읽으면서 자연스럽게 이런 특징들을 떠올렸을 정도로 이해가 되었습니다. 참 좋은 책이라고 생각이 들었고, 하나씩 이해하고 배워간다는 느낌을 받았습니다. 이번 파트도 매우 좋았어요.

참고자료
https://262.ecma-international.org/6.0/#sec-lexical-environments

https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0

https://medium.com/@bdov_/javascript-typescript-execution-vs-lexical-vs-variable-environment-37ff3f264831

profile
개발자로 한걸음 한걸음 가고 있어요.

0개의 댓글