Javasciprt 실행 컨텍스트(execution context)

여는 글

여러분들은 실행 컨텍스트를 잘 아시나요?
저는 아직까지도 실행 컨텍스트를 느낌적으로만 알고있지, 자세하게는 잘 모릅니다.
그렇기 때문에 저도 한번 다시 공부할 겸, 블로그 포스팅도 할 겸 해서 이 글을 작성하게 되었습니다.
다같이 실행 컨텍스트에 대해서 한번 알아보도록 하죠!


실행 컨텍스트(execution context)

실행 컨텍스트는 전역 혹은 함수 내의 작성해 놓은 코드가 실행 가능한 코드(Excutable Code)일 경우, 자바스크립트 엔진이 코드를 실행하기 위해 필요한 환경 정보를 모아놓은 객체입니다.

여기서 환경정보란 아래와 같습니다.

변수(Variable)

  • 변수(Variable)에 대한 정보를 알고 있어야 합니다.
  • 전역 변수, 지역 변수, 매개 변수, 객체의 프로퍼티 등이 변수(Variable) 정보에 속합니다.

인자(Argument)

  • 인자(Argument)에 대한 정보를 알고 있어야 합니다.
  • 인자 객체가 인자(Argument) 정보에 속합니다.
  • 보통 변수 정보와 쌍으로 보는 경우가 많습니다.

Scope

  • 중첩된 스코프 중 내부 스코프에서 외부 스코프에 접근을 위한 정보를 알고 있어야 합니다.

This

  • this값은 함수 호출 패턴에 의해 달라지기에 this가 어떤 객체를 할당하고 있는지에 대한 정보를 알고 있어야 합니다.

이 정보들은 실행에 필요한 정보를 형상화 하고 구분하기 위해 물리적 객체의 형태로 관리합니다.
단, 이 값들은 각 실행 컨텍스트 타입에 따라 달라질 수 있습니다.


코드의 타입

실행 컨텍스트는 자바스크립트 엔진이 동작을 시작하면 그 순간부터 생성되어집니다.
여기서 실행 컨텍스트의 종류가 나뉘어지게 되는데, 각 코드 타입에따라 실행 컨텍스트의 종류가 나뉘어집니다.

전역 코드

  • 전역 영역에 존재하는 코드로 window 객체 내부에 작성된 코드를 의미합니다.
  • 단, 전역에 정의된 함수나 클래스 같은 내부 코드는 전역코드의 포함하지 않습니다.

함수 코드

  • 함수 내에 작성된 코드를 의미합니다.
  • 단, 함수 내부의 중첩된 함수 및 클래스 같은 코드는 호출된 함수코드에 포함하지 않습니다.

eval 코드

  • eval 함수로 실행되는 코드입나다.
  • eval 함수의 악의적 특성(XSS 취약점 등..)으로 인해 ES6+에서는 잘 사용하지 않습니다.

모듈 코드

  • 모듈로 작성된 내부 코드를 의미합니다.
  • 모듈 내부의 함수 혹은 클래스는 모듈 코드에 포함되지 않습니다.

이 4가지의 코드 타입은 각각 실행 컨텍스트를 생성하는 과정과 관리하는 내용이 다릅니다.


실행 컨텍스트 타입

위에서 설명했듯이 실행 컨텍스트는 총 4가지의 코드타입에 따라 실행 컨텍스트 타입이 분류될 수 있습니다.

전역 실행 컨텍스트(Global Execution Context)(GEC)

  • 전역변수를 관리하기 위한 최상위 스코프를 가지는 실행 컨텍스트
  • 자바스크립트 엔진이 전역코드를 평가하면 그 이후 전역 실행 컨텍스트가 생성되어짐
  • 다른말로 전역 객체(Global Object)(GO)라고 불리는 경우도 있음
  • window객체, this객체, 빌트인 객체(MATH, String), BOM, DOM, 전역 변수 정보를 가지고 있음

함수 실행 컨텍스트(Function Execution Context)(FEC)

  • 지역변수, 매개변수, arguments를 관리하기 위해 지역 스코프를 가지는 실행 컨텍스트
  • 자바스크립트 엔진이 함수코드를 평가하면 그 이후 함수 실행 컨텍스트가 생성되어짐
  • 다른말로는 활성 객체(Activation Object)(AO)라고 불리는 경우도 있음
  • 함수 선언식 정보, 함수의 매개 변수 정보, 변수 정보를 가지고 있음

Eval 함수 실행 컨텍스트(Eval Function Execution Contex)

  • 빌트인 함수인 eval 함수가 실행될 때 마다 생성되어지는 컨텍스트
  • Eval 함수의 악의적 특성(XSS 취약점 등..)으로 인해 ES6+에서는 잘 사용하지 않음

모듈 실행 컨텍스트(Module Execution Contex)

  • 모듈내의 독립적인 스코프를 가지는 실행 컨텍스트
  • 자바스크립트 엔진이 모듈을 펼가하면 그 이후 모듈 실행 컨텍스트가 생성되어짐
  • 모듈의 실행이 끝나면 모듈 실행 컨텍스트는 사라짐

실행 컨텍스트 내부 구성

실행 컨텍스트 내부는 ES5 이전과 ES5 이후로 나뉘어 집니다.
그렇기 때문에 ES5이전과 이후를 비교해서 알 필요가 있습니다.

ES5 이전 실행 컨텍스트 구조

ExecutionContext :{
	변수 객체(Variable Object)
	스코프 체인(Scope Chain)
	this 바인딩(Context Object)
}

위에 제가 설명한 자바스크립트 엔진이 코드를 실행하기 위해 필요한 정보가 그대로 실행 컨텍스트 내부에 있는 구조입니다.

  • 변수 객체(Variable Object) : 변수와 인자 정보를 관리
  • 스코프 체인(Scope Chain) : 중첩된 스코프 중 내부 스코프에서 외부 스코프에 접근을 위한 정보 관리
  • this 바인딩(Context Object) : this가 어떤 객체를 할당하고 있는지에 대한 정보 관리

하지만 ES5이후 let과 const같은 block 스코프라는 개념이 추가되면서 이 구조는 더이상 사용하지 않고 새로운 구조로 변경이 되었습니다.

따라서 이러한 내부 구성으로 유지되어 있다는것만 확인후 넘어가도록 하겠습니다.

ES5 이후 실행 컨텍스트 구조

ExecutionContext :{
	LexicalEnvironment:{
		Environment Record
		Outer Environment Reference
	},
	VariableEnvironment:{
		Environment Record
		Outer Environment Reference
	},
	PrivateEnvironment: {
		...
	}
}

ES5이후 블록 스코프 개념이 추가됨으로 인해 Lexical Environment와 Variable Environment가 생겨났습니다. (PrivateEnvironment에 대해서는 아래에서 따로 다루도록 하겠습니다.)


Lexical Environment

Lexical Environment는 실행 컨텍스트 내의 유효한 변수 선언 혹은 함수 선언에 대한 식별자를 맵핑하는데 사용하는 구성요소 입니다.

외부 환경 레코드(Outer Environment Reference)

외부 실행 컨텍스트를 참조하기 위해 사용되어지는 정보로, ES5이전에 스코프 체인의 역할을 이곳에서 수행합니다.

  • 전역 실행 컨텍스트(Global Excution Context) 일 때
    • Outer Environment Reference의 값은 참조할 실행 컨텍스트가 없으므로 null값으로 저장
  • 함수 실행 컨텍스트(Function Lexical Environment) 일 때
    • Outer Environment Reference의 값은 해당 실행 컨텍스트의 상위 실행 컨텍스트를 참조

환경 레코드(Environment Record)

환경 레코드는 변수 선언 및 함수 선언문의 정보를 저장하는 추상 클래스 입니다.

 

여기서 잠깐!

추상 클래스란? 추상 클래스 내 정의된 추상 메소드를 하나 이상 포함한 클래스이며, 구현되어 있지 않은 추상 메소드를 포함하고 있기에 상속을 통해 사용이 가능한 클래스입니다.

 

Environment Record는 추상 클래스이기에, Environment Record를 상속받는 3가지의 서브 클래스가 존재하며,
그 요소는 아래와 같습니다.

  • Global Environment Record
  • Object Environment Record
  • Declarative Environment Record

그럼 위에 설명드린 실행 컨텍스트를 보기 쉽게 정리한 객체는 아래와 같이 정리할 수 있습니다.

ExecutionContext :{
	LexicalEnvironment:{
		Environment Record :{
			Global Environment Record
			Object Environment Record
			Declarative Environment Record
		}
		Outer Environment Reference
	},
	...
}

전역 환경 레코드(Global Environment Record)

전역 환경 레코드는 전역 객체와 빌트인 전역 객체, 스크립트 내의 모든 최상위 객체에 접근할 수 있는 식별자를 제공하는 레코드입니다. realm에서 처리되는 레코드이며, 최상위 스코프를 나타내는데 사용되는 레코드 입니다.

이론상 하나의 레코드 이지만, 실제로는 declarative Environment Record와 object Environment Record 가 합성된 형태로 정의됩니다.

객체 환경 레코드(Object Environment Record)

식별자를 특정 객체의 속성으로 취급 할 때 사용되어지는 객체입니다. BindingObject라는 속성을 가지고 있으며 Environment Record에 바인딩 된 객체를 가리킵니다.
IsWithEnvironment속성도 존재하며, 환경 레코드가 with문에 의해 생성되었는지를 나타내는 불리언 값입니다. 다만 with문 사용은 권장하지 않습니다.

  • 전역 실행 컨텍스트(Global Excution Context) 일 때
    • window 객체 및 전역 빌트인 Object
  • 함수 실행 컨텍스트(Function Lexical Environment) 일 때
    • Outer Environment Reference의 값은 해당 실행 컨텍스트의 상위 실행 컨텍스트를 참조

선연적 환경 레코드(Declarative Environment Record)

선언적 환경 레코드는 var, const, let, class, module, import, function 등으로 스코프 내에서 선언된 식별자들의 바인딩을 관리하는 레코드 입니다.

선언적 환경 레코드는 추가적으로 2가지의 환경 레코드로 분류 할 수 있는데요, 각 각 함수 환경 레코드(Function Environment Record)와 모듈 환경 레코드(module Environment Record) 이 2가지로 분류되어 집니다.

그럼 실행 컨텍스트의 내부 구조는 아래와 같습니다.

ExecutionContext :{
	LexicalEnvironment:{
		Environment Record :{
			Global Environment Record
			Object Environment Record
			Declarative Environment Record :{
				Function Environment Record,
				Module Environment Record
			}
		}
		Outer Environment Reference
	},
	...
}

함수 환경 레코드(Function Environment Record)

함수의 최상위 스코프를 나타내는데 사용되는 선언적 환경 레코드입니다.

  • 화살표 함수 실행 컨텍스트 일 경우
    • this 바인딩 정보를 저장
  • 화살표 함수가 아닐 경우
    • super를 참조하는 경우 super 메소드를 실행하는데 필요한 state를 저장

모듈 환경 레코드(module Environment Record)

Module의 외부 스코프를 나타낼 때 사용하는 선언적 환경 레코드입니다.

변경 가능, 변경 불가능한 바인딩과 더불어 변경 불가능한 import 바인딩(immutable import binding)을 제공합니다. immutable import binding은 참조하는 외부 레코드의 바인딩에 대해서 간접적으로 접근해 변경하지 못하게합니다.

선언적 환경 레코드의 메서드를 전부 구현하지만, GetBindingValue나 DeleteBinding 같이 바인딩에 직접적으로 접근하는 일부 메서드는 구현 스펙이 다릅니다.


Variable Environment

ExecutionContext :{
	LexicalEnvironment:{
		Environment Record :{
			Global Environment Record
			Object Environment Record
			Declarative Environment Record :{
				Function Environment Record,
				Module Environment Record
			}
		}
		Outer Environment Reference
	},
	VariableEnvironment:{
		Environment Record :{
			Global Environment Record
			Object Environment Record
			Declarative Environment Record
		}
		Outer Environment Reference
	},
	...
}

전반적으로 Lexical Environment와 큰 차이가 없지만, Variable Environment는 var키워드로 생성된 변수 정보만 저장하고, Lexical Environment는 let과 const 키워드로 생성된 변수와 함수 선언의 정보를 저장한다는 차이가 있습니다.

그에 따라, Lexical Environment는 코드를 실행하는 과정에서 실행 컨텍스트 내 정보가 변경 변경될 수 있습니다, 하지만 Variable Environment는 항상 값을 유지합니다.


직접 코드로 이해해보자!

console.log(globalValue);
var globalValue = "nowVisible";

function sayHiOneTime() {
    var isMorning = true;
    let hi = "Good morning!";
    while (isMorning) {
        var name = "Jack";
        let question = "How are you?";
        console.log(`${name} ${hi} ${question}`);
        isMorning = false;
    }
}
sayHiOneTime();


접근 제한 환경(Private Environment)

ES2019부터 #prefix가 추가되면서 실행 컨텍스트 구성요로소 추가된 환경입니다. 이 접근 제한 환경 레코드는 class키워드로 생성된 가장 근접한 클래스 내에서 Private 필드 혹은 메소드를 가지는 접근 제한 환경 레코드 정보를 가지는 구성요소 입니다. 만약 가장 근접한 클래스가 없다면 이 구성요소는 null값으로 저장되어집니다.

접근 제한 환경 레코드(Private Environment Record)

접근 제한 환경 레코드(Private Environment Record)는 클래스 선언과 클래스 표현으로 생성되어진 클래스 내 Private 필드 혹은 메소드를 추적하기 위한 레코드 입니다.

추가적으로 접근 제한 환경 레코드는 OuterPrivateEnvironment와 Names라는 구성요소를 가집니다.

  • OuterPrivateEnvironment
    • 외부 Private 필드 혹은 메소드를 가지는 클래스의 실행 컨텍스트를 참조하기 위해 사용되어지는 정보를 저장
    • 만약, 외부에 참조할 클래스가 없다면 null값으로 저장
  • Names
    • 실행 컨텍스트 내의 Private한 식별자 정보를 저장

그럼 전체적인 실행 컨텍스트의 내부 구조를 객체로 표현하면 아래와 같아집니다.

ExecutionContext :{
	LexicalEnvironment:{
		Environment Record: {
			Global Environment Record
			Object Environment Record
			Declarative Environment Record :{
				Function Environment Record,
				Module Environment Record
			}
		}
		Outer Environment Reference
	},
	VariableEnvironment:{
		Environment Record: {
			Global Environment Record
			Object Environment Record
			Declarative Environment Record
		},
		Outer Environment Reference
	},
	PrivateEnvironment: {
		PrivateEnvironmentRecord: {
			OuterPrivateEnvironment,
			Names
		}
	}
}

마무리

이렇게 실행컨텍스트 내용을 정리하였습니다.
꽤 어려운 내용이라 처음에는 이해가 되질 않았지만, 정리를 하면서 많은것을 배울 수 있었습니다.
만약 잘못된 내용이나 틀린 내용이 존재한다면 댓글로 남겨주시면 감사합니다.

참고문서

이 글을 공유하기

댓글