Execution Context(실행 컨텍스트)

kirin.log·2021년 6월 1일
0

🎃 실행 컨텍스트(Execution Context)

  • 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념. 즉, 실행 가능한 코드가 실행되기 위해 필요한 환경

  • 실행 컨텍스트(Execution Context)는 scope, hoisting, this, function, closure 등의 동작원리를 담고 있는 자바스크립트의 핵심원리.

  • 일반적으로 실행 가능한 코드는 전역 코드와 함수 내 코드이다.

    • 전역 코드 : 전역 영역에 존재하는 코드
    • 함수 코드 : 함수 내에 존재하는 코드
    • Eval 코드 : eval 함수로 실행되는 코드(사용도 적음)

Types of Exection Context(실행 가능한 코드의 종류)

  • Global Execution Context(전역 코드) ➡ 가장 베이스가 되며, 가장 먼저 실행되는 구역이다. 특정 '함수' 안에서 실행되는 코드가 아니라면 코드는 전역 컨텍스트에서 실행된다.
    전역 컨텍스트에서는 두 가지 일이 이루어지는데 1) window 오브젝트인 전역 컨텍스트를 생성하고 2) this 를 global object로 할당한다.
    글로벌 컨텍스트는 프로그램에 오직 하나만 존재한다.
  • Functional Execution Context(함수 코드) ➡ 글로벌 컨텍스트가 코드를 실행하다가 함수 실행문을 만나면 함수 실행 컨텍스트가 생성되고 스택이 형성된다. 함수가 호출될 때마다 해당 함수에 대한 실행 컨텍스트가 생성된다. 각각의 함수들은 자신만의 실행 컨텍스트를 가지지만, 실행 컨텍스트는 함수가 호출이 되어야 만들어진다.
  • Eval Function Execution Context(eval 코드) ➡ eval 함수 또한 자신 만의 실행 컨텍스트를 가진다. 하지만 eval는 자바스크립트 개발자가 많이 사용하지 않는 개념
  • 코드를 실행하기 위해 실행에 필요한 여러가지 정보는 아래와 같다.
    • 변수 : 전역변수, 지역변수, 매개변수, 객체의 프로퍼티
    • 함수 선언
    • 변수의 유효범위(Scope)
    • this

📌 실행 컨텍스트 스택(Stack) ( = Execution Stack (Call Stack) )
Execution stack은 런타임에 실행 컨텍스를 저장하기 위해 자바스크립트 엔진에 의해 생성되고 관리되는 Last-In-First-Out(LIFO) 스택의 자료구조이다.
💡 LIFO (Last In First Out, 후입 선출)의 구조를 가지는 나열 구조.

var x = 'xxx';

function foo () {
  var y = 'yyy';

  function bar () {
    var z = 'zzz';
    console.log(x + y + z);
  }
  bar();
}
foo();

📌 Execution Stack (호출 스택)과 함수 실행 순서

1) 스택은 LIFO(Last in, First out) 자료 구조로 코드가 실행하면서 만드는 실행 컨텍스트들이 저장되는 구조이다.

2) 자바스크립트 엔진이 script tag를 처음 만나면 전역 컨텍스트를 만들고, 현재 실행되고 있는 호출 스택에 이를 push 한다.
다른 함수가 호출 되면 해당 함수에 대한 실행 컨텍스트를 생성하고, 실행된 코드 블록(스택)의 제일 꼭대기에 쌓인다 ( push ).
💡 전역 코드(Global code)로 컨트롤이 진입하면 전역 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 쌓인다. 전역 실행 컨텍스트는 애플리케이션이 종료될 때(웹 페이지에서 나가거나 브라우저를 닫을 때)까지 유지된다.

3) 자바스크립트 엔진은 실행 컨텍스트가 호출 스택에서 가장 위에 있는 함수를 실행하며, 함수가 할 일을 마치면 스택에서 제거된다 ( pop ).

🚫 함수 호출 정리
1. 함수를 호출하면 이에 맞춰서 함수를 실행할 수 있는 환경을 만들고 초기화한다.
2. this를 바인딩한다. (this가 어떤 객체를 참조해야 할지 결정한다.)
3. 실제 함수 코드를 수행하고 그 결과를 result에 저장한다.
4. 이 함수를 호출했던 곳(환경)으로 돌아가며, result를 반환한다.


🎃 자바스크립트 엔진이 실행 컨텍스트(Execution Context)를 만드는 과정

ES5 이전 실행 컨텍스트는 변수객체(VO), Scope Chain, this 바인딩 3요소로 구조를 해석했으나, ES5부터는 Lexical Environment 개념으로 재정의

Lexical Environment(렉시컬 환경) 은 특정 코드가 작성, 선언(정의)된 환경을 의미하며, 객체이다.

🐹 실행 컨텍스트(Execution Context)는 두 단계로 생성된다.
1) Creation Phase(생성) 와 2) Execution Phase(실행) 이다.

Creation Phase 단계에서는, 일단 코드를 쓰윽 스캐닝하며 어떤 파라미터와 변수들이 있는지, 어떤 함수들이 있는지 (실제 아직 그것이 어떤 값을 갖게 되는지, 어떤 내용의 함수인지는 모르지만), 어떤 스코프를 갖게되는지, this를 어떻게 바인딩할 것인지 등등의 정리를 한다.

정리가 끝나면 Execution Phase 단계가 시작된다. execution stack (흔히, 콜 스택이라 부르는)에 정리한 내용을 push 한 후 본격적으로 정리한 일련의 정보 객체를 바탕으로 코드를 실행한다. 변수를 만나면 아까 execution stack에 정리한 내용의 변수 관련 오브젝트에 변수 값을 맵핑한다.

함수 실행문을 만나면 위의 동작들을 반복하고, 실행이 종료되면 execution stack에 쌓여있는 execution context는 스택에서 소멸되고 다음 execution context의 실행을 하며 모든 execution context의 실행이 끝날 때까지 반복한다.

(1) Creation Phase

Creation phase 동안 두 가지 일이 일어난다.
1) LexicalEnvironment 컴포넌트 생성 ➡ JS 코드에서 식별자(변수, 함수)를 정의하는 데 사용.

2) VariableEnvironment 컴포넌트 생성 ➡ 비슷함 (var만 적용)

실행 컨텍스트는 개념적으로 아래와 같다.

ExecutionContext = {
  LexicalEnvironment = <ref. to LexicalEnvironment in memory>,
  VariableEnvironment = <ref . to VariableEnvironment in memory>,
}

🏈 Lexical Environment
Lexical Environment는 identifier(참조대상 식별자)인 함수/변수명과 해당 식별자에 대입된 값을 매핑하는 곳 (함수, let/const)
참조 대상 식별자(identifier)는 함수와 변수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별할 수 있는 유일한 이름이다. 자바스크립트는 이 규칙대로 식별자를 찾는다. 즉 Lexical Environment에서는 변수와 해당 변수에 대입된 값이 매핑되는 곳이라고 할 수 있다. 매핑만 됨 !

var a = 20;
var b = 40;

function foo(){
	console.log('bar');
}

위의 코드에 대한 lexical environment는 아래와 같다.

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

🌈 렉시컬 환경은 세가지 일을 한다. (VariableEnvironment도 동일)
1. Environment Records (변수/함수선언 저장, 글로벌 오브젝트도 기록)
2. Reference to the outer environment (외부 렉시컬 환경을 저장(스코프 체인))
3. This binding (기존처럼 전역은 global(or undefined), 함수는 선언법에 따라 바인딩)

  1. Environmnet Records
    lexical environment 안에 함수와 변수를 기록한다.
  • Declarative environment record -- 변수와 함수 선언을 저장하는 곳이다.
  • Object environment record -- 전역 코드에 대한 lexical environment는 objective environment records를 포함한다. 변수와 함수의 선언과 다르게 object environment record는 글로벌 오브젝트도 기록한다. 각각의 객체의 속성을 바인딩하기 위해서 record에 새로운 엔트리가 형성된다.
  1. Reference to the outer environment
    외부 환경으로의 참조 값의 의미는 외부 lexical 환경으로 접근할 수 있다는 의미이다. 자바스크립트 엔진이 현재의 lexical environment에서 변수를 찾지 못했다면 외부 환경에서 해당 변수를 찾아 볼 수 있다는 의미이다.

  2. This binding
    this의 값이 여기서 결정된다. 글로벌 실행 컨텍스트에서 this는 global object 이다.
    함수 실행 컨텍스트에서는 this 값은 어떻게 함수가 호출되었는지에 따라 달라진다. 만약 함수가 object reference로 호출되었다면 this는 해당 객체를 가리키게 된다. 그렇지 않으면 this는 글로벌 객체(window)를 가리키거나 strict mode에서는 undefined를 가리키고 있다.

this와 함수 호출

const person = {
  name : 'peter',
  birthYear : 1994,
  calcAge: function(){
	console.log(2018- this.birthYear);
	}
}
person.calcAge();

const calcuateAge = person.calcAge;
calculateAge();
  • person.clacAge() ➡ calcAge는 'person' object reference로 호출되었기 때문에 여기서 "this"는 'person'을 가리키게 된다.
  • calculateAge() 여기서는 주어진 객체 참조값이 없기 때문에 this는 글로벌 window 객체를 가리키게 된다.

🏈 VariableEnvironment
LexicalEnvironment 와 funtion, 변수 식별자가 binding 되는 점을 포함해 동일하다.
VariableEnvironment 또한 Lexical Environment이다.
ES6 에서 LexicalEnvironment와 VariableEnvironment 둘의 차이점은 전자가 함수 선언과 변수 (let과 const)의 바인딩을 저장하고 후자는 변수 var 만 저장한다.
var 변수의 선언과 초기화가 여기서 동시에 일어나기 때문에, Hoisting이 발생하게 되는 것

✅ 즉, 실행 컨텍스트의 Creation Phase 에서는

  • Scope Chain, 변수, 함수, 인자들을 만든다.
  • this 를 결정한다.
  • 자바스크립트 엔진의 Syntax Parser가 코드를 읽으면서 컴퓨터가 알아들을 수 있는 언어로 변환된다.
  • 자바스크립트 엔진은 코드를 읽으면서 변수와 함수의 선언된 것을 찾고 메모리에 해당 변수와 함수를 저장한다. (호이스팅)


(2) Execution Phase

자바스크립트 엔진이 한줄 한줄 위에서 부터 코드를 읽으면서 코드를 실행할 차례이다. 이 단계에서 변수들에 값이 할당된다.

❗ 이때 letconst는 LexicalEnvironment의 Environment record에 식별자가 생성(변수 선언)되며 초기화가 되지 않지만, var의 경우 선언과 초기화(undefined)가 일어난다.

실행 컨텍스트의 Creation Phase + Execution Phase 전체 과정을 정리하면 아래와 같다.

let a = 20;
const b = 30;
var c;

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

1) 자바스크립트 엔진은 script를 만나면 전역 컨텍스트를 만든다. 전역 변수 a,b, multiply는 LexicalEnvironment에 c는 VariableEnvironment에서 이름: 값 매핑이 된다.

2) 전역 컨텍스트의 Execution Phase에서 코드가 실행되고 각각의 변수에 값이 할당된다.

3) multiply() 함수가 호출되는 순간 multiply() 함수의 실행컨텍스트가 만들어진다.(Creation Phase 시작)
외부 환경으로는 전역 컨텍스트를 참조하고 있고, object reference로 호출되지 않았기 때문에 여전히 this는 글로벌 오브젝트이다.

4) multiply 함수가 값을 리턴하면서 호출 스택에서 빠진다. 전역 컨텍스트에 있던 전역 변수 c에 값이 업데이트 된다. 그 후 글로벌 코드가 끝나면 프로그램도 종료된다.
함수실행이 종료되면 콜스택에서 빠져나온다.

letconst는 실행 컨텍스트가 만들어질 때 (creation phase), 어떤 값도 가지고 있지 않다. 하지만 varundefined를 가지고 있다.

그 이유는 실행 컨텍스트가 만들어지는 동안, 코드는 변수와 함수 선언을 위해 스캔된다. 이때 함수의 선언은 environment에 함수 전체가 다 저장되지만, 변수들은 기본 값으로 undefined나 아직 초기화 되지 않은 상태로 저장된다. (호이스팅 개념)
(생성 단계(Creation Phase)일 때, Lexical Environment를 생성하면 스코프의 변수와 함수 선언되는데, 이 때, let과 const는 선언만, var는 선언 + 초기화까지 된다. + 함수 선언문은 선언, 초기화, 할당까지)

이 때문에 var 변수가 선언되기 전에 undefined라는 값으로 접근 할 수 있는 것이고, letconst를 선언하기 전에 접근하면 reference error를 얻게 되는 것이다.

이것을 호이스팅이라고 부른다.

execution phase 동안 자바스크립트 엔진이 소스 코드에서 let 변수의 값이 선언된 곳을 찾지 못하면 undefined를 할당한다.

profile
boma91@gmail.com

0개의 댓글