Javascript에서 가장 중요한 개념중 하나인 ExecutionContext(실행컨텍스트)는 자바스크립트엔진이 자바스크립트를 실행할 때 실행할 코드에 제공할 환경 정보들을 모아놓은 객체 이다.
여기서 말하는 정보는 변수, 함수 선언, 변수의 유효범위(Scope), this와 같은 것들이 있다.
EC의 종류 크게 GlobalContext(전역)과 FunctionalContext(함수) 그리고 EvalContext로 나뉜다.
전역 컨텍스트는 자바스크립트 코드를 실행할 때 단하개만 정의되는 컨텍스트이다. Global Object(전역 객체)를 생성하며 this 값에 전역객체를 참조한다. 전역 컨텍스트는 App 실행 시 가장먼저 추가되며 후술할 CallStack에 의해 가장 나중에 제거된다.
함수 컨텍스트는 함수가 실행될 때 매번 정의되는 컨텍스트이다. 함수 컨텍스트는 전역 컨텍스트와는 다르게 함수가 실행될 때 마다 정의되고, 또 함수의 실행이 종료될 시(return) CallStack에서 제거된다.
EvalContext는 eval함수로 실행한 코드의 context이다. 그러나 보안상의 문제도 있고, MDN에서도 절대 사용하지 말것을 권장하고 있기에 있다는 것만 알아둘 예정이다.
출처 - MDN eval()
Stack이란 데이터를 차곡차곡 쌓아올리고, 마지막으로 쌓인 데이터부터 꺼낼 수 있는 LIFO(Last In First Out, 후입선출) 형식의 자료구조이다. (데이터를 쌓는것을 push, 꺼내는것을 pop 이라한다.)
실행 컨텍스트는 자바스크립트 엔진으로부터 CallStack으로 관리되게 되는데 다음 예제를 보며 이해 해보자.
var name = "Cain";
function nameCall(values) {
console.log("values = ",values);
nameReCall(values);
}
function nameReCall(reValues) {
console.log("reValues = ",reValues);
}
nameCall(name);
위와 같은 코드가 있다고 가정할 때, 먼저 전역컨텍스트가 CallStack에 할당(push)되게된다. 이후의 순서를 살펴 보자.
이러한 순서를 통하여 Stack형식으로 엔진이 자바스크립트를 실행하게 된다. 그리고 그 결과 값은 아래와 같다.
먼저 values = Cain이라는 순서 4번에 해당하는 값이 찍히고, 그 이후 6번 reValues = Cain 이 찍히는 모습을 볼 수 있다.
실행 컨텍스트는 물리적으로 객체의 형태를 가지며, 3가지 프로퍼티를 갖고 있다.
변수 객체(Variable Object)는 실행에 필요한 정보를 담은 객체를 생성하는데 이 객체를 변수 객체 라고한다.
변수 객체는 변수, 함수선언(표현식X) 매개변수와 인수정보 와 같은 정보들을 담고 있다.
변수 객체는 크게 전역 컨텍스트와 함수 컨텍스트에서 각각 가르키는바가 틀린 부분이 있는데, 먼저 전역 컨텍스트에서는 전역변수와 전역함수를 프로퍼티로 소유한 전역 객체(Global Object)를 가르킨다.
함수 컨텍스트에서는 지역변수, 내부함수, 매개변수, 인수정보를 배열의 형태로 담고있는 활성 객체(Activation Object)를 가르킨다.
스코프 체인(Scope Chain)은 일종의 리스트로써 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고, 각각의 스코프가 어떻게 연결 되고 있는지를 보여주는것이다.
// Code 1
let a = "This is A";
function whatA() {
let a = "It's A?";
function whatIts() {
console.log(a);
}
whatIts();
}
whatA(); // It's A?
// Code 2
let a = "This is A";
function whatA() {
// let a = "It's A?";
function whatIts() {
console.log(a);
}
whatIts();
}
whatA(); // This is A
위와 같은 두 코드가 있다고 가정 해 보자. 우리는 앞서 실행컨텍스트가 어떤식으로 관리되는지 보았을것이다.
Code 1은 CallStack에 의해 a값을 찾을때 자신이 소속된 함수의 스코프내에서 a값을 찾게된다.
그리고 let a = "It's A?"; 라는 지역변수 a를 찾게되어 해당 값을 출력하는 모습을 볼 수 있다.
그러나 Code 2에서는 지역변수 a를 찾을 수 없음으로 상위스택인 전역스코프를 참조하여 let a = "This is A";라는 전역변수 a를 찾게되어 해당 값을 출력하는 모습을 볼 수 있다.
이런식으로 엔진이 해당 컨텍스트의 스코프가 어떻게 연결되어있는지를 탐색하는 과정들을 스코프 체인이라고 볼 수 있다.
this란 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는(값을 참조하는) 식별자이다. 실행컨텍스트가 생성되는 과정에 있어 아래 후술할 생성단계 의 마지막에 this의 값이 할당되는데, 이때 참조하는 값이 없거나 this의 값이 결정되기 이전에는 this는 전역객체를 가리키게된다.
상황에 따른 this를 알아보자.
let propertys = {
name: "test",
thisis: function() {
console.log(this);
console.log(this.name);
}
};
propertys.thisis(); // {name: 'test', thisis: function}
// test
propertys의 메서드thisis의 this는 자신이 속한 객체 propertys를 가리키는 걸 볼 수 있다.
var test = "this is test";
console.log(window.test);
var testCall = function(){
console.log(this.test);
}
testCall();
// this is test
// this is test
function test(a,b) {
this.a = a;
this.b = b;
}
var createTest = new test("test", 0);
createTest.a; // test
createTest.b; // 0
실행 컨텍스트의 생성단계에서는 변수객체 혹은 활성객체가 생성되고 호이스팅이 일어나며, 스코프체인이 생생되고, this의 값이 결정된다.
실행 컨텍스트의 실행단계에서는 생성된 변수의 값이 할당되고, 코드가 실행된다.