Execution context

2020. 10. 22. 11:34Tech Article/Javascript

들어가기에 앞서..

본 내용은 한번 읽고 이해하기 쉽지 않습니다. 다소 어려운 내용임을 감안하고 읽어주시기 바랍니다.
javascript engine의 동작순서로 다소 추상적인 내용으로 다가올 수 있습니다.
이 글은 내용은 최대한 짧게 용어위주로 요약한 글입니다. 이해가 되지 않는다면 키워드별로 검색을 해서 상세하게 이해하시는 것을 권장하고 싶습니다.

 Execution context(EC)는 javascript 코드가 실행되는 방식입니다.

(javascript engine이 동작하는 방식)

먼저 아래에서 다룰 term(용어)부터 정리해보겠습니다.

(영문과 한국어를 자주 섞어쓰는 이유는, 두가지 언어가 어떻게 호환되는지 알아야 구글링이 원활하기 때문입니다. 불편하더라도 양해부탁드립니다.)

 

===    실행되는 환경    ===

Execution Context (EC)

  • FEC (Functional Execution Context)
  • GEC (Global Execution Context)
  • Eval (Execution Context inside eval function)
  • ECS (Execution context stack)

 

===    실제 동작되는 순서    ===

Phase

Creation Phase

  • Activation Object
  • argument object, variable object
  • Scope chain
  • this value

Execution Phase

  • update/initialize value
  • execution code

 

 

시작부터 용어가 엄청나게 많습니다. 매우 당황스럽지만, 순서대로 천천히 살펴봅시다. 😭

 


Execution context

 GEC(Global Execution Context), FEC(Functional Execution Context), Eval(Execution context for eval function)로 코드가 실행되는 환경(영역)이라고 생각하면 됩니다.

 

GEC (Global Execution Context)

 Global Execution Context가 가장 기본적으로 load되며, GEC는 1번 이상 존재할 수 없습니다. javascript는 single thread이기 때문에, global 환경은 하나만 존재할 수 있기 때문입니다. stack memory의 가장 아래에 들어있게 됩니다. 모든 javascript 코드 실행이 끝나야 stack에서 pop out(제거) 됩니다.

아래 코드는 Global Execution Context만 존재하는 경우입니다.

var name='stefan'

console.log(name)

 

FEC (Functional Execution Context)

 Function call이 있을 때마다 load(혹은 생성)됩니다. 1번 이상 존재할 수 있습니다.  FEC(하위)는 GEC(상위) 환경에 접근할 수 있습니다. (이것은 Lexical Scope 개념으로 이해할 수 있습니다.), 하지만 반대로 GEC(상위)에서 FEC(하위)로의 접근은 허용 되지 않습니다.

아래코드는 Global Execution과 Functional Execution이 존재하는 경우입니다.

var name='stefan'

console.log(name)

function foo(){


}

foo() //functional execution

 

Eval (Execution context inside eval function)

 eval function내의 context입니다. 아직 정리가 되지 않은 부분으로 추후에 업데이트 하겠습니다. 

 

ECS (Execution Context Stack)

 javascript가 실행되는 과정에서 memory에 data를 stack에 쌓게되는데, Execution Context Stack은 코드가 실행되면서 stack에 쌓이게 되는 structure(구조)를 의미합니다. stack에 데이터가 store(저장)되고 LIFO(Last In First Out)형태로 코드가 실행됩니다.

아래 코드를 stack에 넣으면

var name='stefan'

console.log(name)

function foo(){


}

foo() //functional execution

Execution Context Stack

 위 그림을 보면 Stack이 비어있는 상태에서 최초에 Global Execution Context가 가장 바닥에 들어가고, foo( ) 코드 줄에서 Functional Execution Context가 stack에 쌓이게 되고, LIFO(Last In First Out)으로 하나씩 pop out(제거) 된다.

 

 


이제 예제 코드로 Phase(단계)의 순서를 알아보겠습니다.

아래 예제로 설명하도록 하겠습니다.

var firstName="stefan"
var lastName="cho"

function foo(first, last){
	var age=30
	return first + last
}

foo(firstName, lastName)

위 예제의 Phase 동작 순서

Phase

 2가지 phase(단계)가 있습니다. 첫번째는 Creation Phase(생성 단계)로 코드를 읽고, 그 다음 Execution Phase(실행 단계)로 코드를 실행합니다.

앞서 Execution Context(EC)가 코드 실행되는 환경(범위, 영역)라고 하면, Phase는 실제로 어떤 단계로 환경내에서 코드가 진행되는지를 나타냅니다.

 

Creation Phase (GEC, Global Execution Context)

 말그대로 생성단계 입니다. 코드를 실행하지는 않고, 정해진 부분을 compile(컴파일, 한곳에 모아 묶는 것을 의미)합니다. function을 call했다면 (혹은 invoked 되었다면), function내부로 들어가서 필요한부분을 저장합니다. function 내부로 들어가지만 이것은 function실행을 의미하는 것은 아닙니다. Javascript engine은 executionContextObj(Activation Object 같은 의미)를 생성하고 이 object내에 argument, variable, scope chain, this value들을 저장합니다. 

 

Activation Object (AO)

 아래와 같은 형태로 object를 생성합니다. 여기에 argument variable, variable object 들을 아래와 같은 형태로 저장합니다. (object의 이름은 임의 붙인것이니 신경쓰지 마세요)

만약 function definition이 있다면, 이것은 heap memory에 저장합니다. (execution context stack에 저장하지 않습니다.)

executionContextObject = {

	variableObject: {}, //argument와 variable을 저장
	scopeChain: [], //scope chain을 저장
	this // this value 저장

}

 

argument variable, variable Object

 function의 argument 그리고 variable object를 Activation Object에 저장합니다.

globalExecutionContextObject={
   activationObject = {
      argumentObject: {//argument와 variable을 저장
        length:0
      }, 
      firstName: undefined,
      lastName: undefined,
      foo: //pointer to function definition
	},						
   scopeChain: [],
   this: {}
}

 

Scope chain

 scope chain을 형성합니다. 예를 들어 function A가 있다면, function A는 global과 scope chain을 형성합니다. (즉 이것은 function A가 global에 접근할 수 있음을 의미합니다.) function A() { function B } 의 형태라면 function B는 function A, global에 scope chain을 형성하고 접근할 수 있습니다. 

globalExecutionContextObject={
   activationObject = { 
      argumentObject: {
        length:0,
      }, 
      firstName: undefined,
      lastName: undefined,
      foo: //pointer to function definition
	},			
   scopeChain: [Global execution context variable object], //scope chain을 저장
   this: {}
}

initialize the value of this

 마지막으로 this value를 initialize합니다.

 

Execution Phase (GEC, Global Execution Context)

 Creation단계가 끝나면, Execution 단계가 됩니다. 여기서는 우선 variable을 update혹은 initialize합니다.

globalExecutionContextObject={
   activationObject = {
      argumentObject: {
        length:0
      }, 
      firstName: "stefan", //Execution Phase에서 값이 update(혹은 initialize)됨
      lastName: "cho", 
      foo: //pointer to function definition
	},						
   scopeChain: [Global execution context variable object],
   this: {}
}

 

그리고 실제 코드의 실행부분이 작동됩니다. stack memory에 실행순서를 쌓고 LIFO (Last In First Out)되는 단계가 바로 이 단계라고 볼 수 있습니다.

우리는 Creation Phase에서 variable object를 declaration(선언)하였고, 실행시킬 function 주소들을 heap memory에 참조해두었습니다.

Creation Phase (FEC, Functional Execution Context)

Global에서의 Phase가 끝나면, Function 내부에서 Creation Phase와 Execution Phase가 일어납니다.

 

Activation Object (AO)

 Activation Object는 Global에서와 마찬가지로 만들어 줍니다. 

 

argument variable, variable Object

 function의 argument 그리고 variable object를 Activation Object에 저장합니다.

fooFunctionExecutionContextObject={
   activationObject = {
      argumentObject: {//argument와 variable을 저장
        0: first,
        1: last,
        length:2
      }, 
    first: firstName,
    last: lastName,
    age: undefined
    },	
   scopeChain: [],
   this: {}
}

 

Scope chain

 scope chain을 형성합니다. foo Function은 상위단계인 Global Execution Context와 chain관계를 만듭니다. (상위에서 하위로는 안됩니다. 이 부분이 이해가 안되면 Lexical Scope 개념을 먼저 이해해야 합니다.)

fooFunctionExecutionContextObject={
   activationObject = {
      argumentObject: {//argument와 variable을 저장
        0: first,
        1: last,
        length:2
      }, 
    first: firstName,
    last: lastName,
    age: undefined
    },	
   scopeChain: [fooFunction variable object, Global execution context object],
   this: {}
}

initialize the value of this

 마지막으로 this value를 initialize합니다.

 

Execution Phase (FEC, Functional Execution Context)

Creation단계가 끝나면, Execution 단계가 됩니다. 여기서는 우선 variable을 update혹은 initialize합니다.

fooFunctionExecutionContextObject={
   activationObject = {
      argumentObject: {
        0: first,
        1: last,
        length:2
      }, 
    first: firstName,
    last: lastName,
    age: 30 // initialize local variable
    },	
   scopeChain: [fooFunction variable object, Global execution context object],
   this: {}
}

 

 

Scope chain도 준비되었기 때문에, 만약 foo에서 사용할 age라는 변수값이 내부에서 안보일 때 global로 접근하여 찾아보면 됩니다. 

 

Function이 실행되는 경우

예제의 동작 순서


순서로 javascript engine이 작동됨을 알 수 있습니다. 만약 foo안에 bar이라는 function이 실행된다면

foo내부에 bar함수 실행코드가 있을 경우


와 같은 순서로 진행이 될 것입니다.

 


💡 Conclusion

Creation Phase는 실행하기에 앞서 셋팅하는 단계라고 생각할 수 있습니다.

Execution은 주어진 셋팅을 바탕으로 한줄 한줄 실행하는 것입니다. 

 

비유를 하자면 John이 손님을 초대하여 파티를 하려고 합니다.

 John이 필요한 각 방에 필요한 음식리스트를 들고오면 Creation Phase에서는 각 방에 리스트대로 음식을 준비하고 정해진 테이블에 올려놓습니다.(Activation Object 단계, declaration을 Activation Object에 저장)

그리고 룸의 관계를 설정합니다.

이때 a룸에서 음식이 없으면 b룸에서 찾으라고 적어두는 것입니다. (Scope Chain을 Activation Object에 저장)

 

이제 손님이 들어올 시간입니다. Execution Phase단계로 들어갑니다. 손님들은 순서대로(코드가 한줄씩 순서대로 실행, variable update나 function call이 되는 코드들이 실행됨) 들어가며, 각 방에 들어가서 음식을 먹습니다.

이 때 필요한 음식이 안보이면 Scope Chain을 참고하여 찾으러 갑니다.(실제 코드에서 scope chain을 하위에서 상위 hierarchy(계층)로만 접근이 됩니다.)

 

📚 참고자료 

Execution context, Scope chain, and Javascript internals

실행 컨텍스트와 자바스크립트의 동작 원리