1) 실행 컨텍스트(Execution Context)란?
ECMAScript 스펙은 실행 컨텍스트를 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이라고 정의합니다. 즉, 실행 가능한 코드가 실행되기 위해 필요한 환경이라고 할 수 있는데, 추상적인 개념이다 보니 확 와 닿지는 않습니다. 좀 더 단순하게 말하자면, 자바스크립트는 내부적으로 하나의 콜 스택을 가지는데, 이 콜 스택 내에 쌓이는 함수에 대한 정보를 실행 컨텍스트라고 합니다.
여기서 쌓이는 함수, 즉 실행 가능한 코드는
- 전역 코드 : 전역 영역에 존재하는 코드 => 전역 실행 컨텍스트
- Eval 코드 : eval함수로 실행되는 코드
- 함수 코드 : 함수 내에 존재하는 코드 => 함수 실행 컨텍스트
를 말합니다. 그러나 eval 코드는 사용을 지양하는 것이 좋고, 전역 코드를 사용하기보다는 함수 코드를 사용하는 것이 바람직한 코드 작성법이기 때문에, 대부분의 경우 실행 가능한 코드란 콜 스택에 쌓이는 함수 코드 정보를 의미합니다.
이렇게 쌓여 있는 코드를 실행하기 위해서는 몇 가지 정보가 있어야 하는데 이는 아래와 같습니다.
- 변수 : 실행컨텍스트와 연관된 데이터를 담고 있는 객체. 해당 실행컨텍스트에서 사용하게 될 전역변수, 지역변수, 매개변수, 객체 프로퍼티, 함수 선언 등을 저장
- Scope chain : 일종의 리스트로 중첩된 함수의 스코프 레퍼런스를 차례로 저장.
- this : 함수를 실행한 인스턴스(함수를 소유한 객체를 의미하지 않음).
2) 실행컨텍스트 스택
위에서 실행 컨텍스트란 콜 스택 내에 쌓이는 함수의 정보라고 언급하였습니다. 코드가 실행될 때, 실행 컨텍스트의 스택이 생성되고 소멸하게 되는데, 현재 실행 중인 컨텍스트 내에서 이 컨텍스트와 관련 없는 코드(예를 들면, 다른 함수)가 실행되면 새로운 컨텍스트가 생성됩니다. 그리고 새롭게 생성된 컨텍스트가 스택에 들어가고, 이 컨텍스트로 제어권이 이동됩니다. 이를 코드로 좀 더 자세히 살펴보도록 하겠습니다.
var global = 'globalVar'
function foo() {
var fooVar = 'fooVar'
console.log(global)
function bar() {
var barVar = 'barVar'
}
bar()
}
foo()
위 코드를 실행하면 아래와 같은 실행 컨텍스트 스택이 생성되고 소멸됩니다. 코드의 실행에 따라 스택이 어떻게 쌓이는지 살펴보면 아래와 같습니다.
애플리케이션의 실행 시 전역 실행 컨텍스트가 스택에 쌓이고 논리적으로 실행순에 따라 함수 실행 컨텍스트가 쌓이고 파기됩니다.
2) 실행 컨텍스트 생성
실행 컨텍스트의 생성과 파기를 좀 더 자세히 들여다보겠습니다. 실행 컨텍스트는 아래와 같은 순서로 생성되고 파기됩니다.
(1) 활성 객체 생성
실행 컨텍스트가 생성되면 자바스크립트 엔진은 이 컨텍스트가 실행되기 위해 필요한 여러 정보를 담는 객체를 생성하는데 이를 활성 객체라고 부릅니다(활성 객체는 엔진 내부에서만 접근할 수 있고 사용자가 접근할 수는 없습니다).
활성 객체가 생성되고 나면, 함수를 호출할 때 넘어온 인자들이 배열 형태로 저장된 argument 객체를 생성합니다. 활성 객체는 이 argument 객체를 참조해 파라미터를 사용합니다(전역 컨텍스트의 경우에는 arguments 객체를 가지지 않습니다).
(2) 스코프 체인 생성
현재 실행 컨텍스트의 스코프 정보가 생성됩니다. 이는 현재 컨텍스트에서 식별자를 찾기 위해 범위 정보를 가지고 있는 객체입니다. 최상위는 window 전역 객체이고, 스코프 정보를 통해 현재보다 더 상위의 실행 컨텍스트의 변수에도 접근할 수 있습니다. 이전에도 언급했듯이 [[scope]] 프로퍼티를 참조하여 접근할 수 있습니다.
(3) 변수 객체 생성
실행 컨텍스트 내부에서 사용되는 지역 변수의 생성이 이루어집니다. 이 지역 변수를 저장하는 역할을 수행하는 것이 변수 객체인데, 위에서 생성한 활성 객체가 변수 객체로 사용됩니다. 전역 컨텍스트의 경우 window가 변수 객체 역할을 합니다. 이 시점에서 변수들은 단지 메모리에 생성될 뿐 초기화가 실행되지 않습니다. 따라서 실제 값이 아닌 undefined 값으로 세팅됩니다(호이스팅).
모든 변수 객체 생성이 이루어지면 변수나 함수의 표현식이 실행되며, 이 시점에 실제 초기화 작업이 진행됩니다. 즉, 실행컨텍스트의 param1, param2는 파라미터로 넘어온 값이므로 이미 초기화되어 있지만, 지역 변수로 선언된 a, b는 아직 undefined 상태로 메모리만 할당되어 있습니다.
(4) this 바인딩
모든 실행 컨텍스트와 관련되어 있으며, this 키워드를 사용하는 값이 할당됩니다. this는 함수를 호출한 인스턴스를 가리키며, 함수가 어떻게 호출되었는지에 따라 값이 달라집니다. 만약 this가 참조하는 객체가 없으면, 전역 객체인 window를 참조합니다.
1. 전역에서 생성한 함수 실행 컨텍스트 내의 this | 2. 객체 인스턴스 내의 this | 3. 콜백함수 내의 this |
- 전역 실행 컨텍스트에서 생성된 foo와 bar는 이 함수를 호출하는 인스턴스가 전역이기 때문에 this가 window입니다.
- 객체의 경우 전역에 생성하더라도 함수를 호출하는 것이 객체 인스턴스이기 때문에 this는 foo입니다.
- 자바스크립트의 경우 매개변수로 함수를 전달할 수 있는데, 이러한 콜백 함수들은 자신을 호출하는 invoker 함수에 따라 this가 변경됩니다. 호출되는 위치가 전역 실행 컨텍스트 내에 존재하는 invoker이기 때문에 callback 함수의 this는 window입니다.
(5) 코드 실행
표현식이 실행됩니다. 이때 변수 객체에 생성되었던 변수의 초기화 및 연산, 함수의 실행 등이 이루어집니다.
(6) 실행컨텍스트 파기
함수 코드의 실행컨텍스트의 함수 실행이 끝나면 해당 함수의 실행 컨텍스트를 파기하고, 애플리케이션 종료 시 전역 실행 컨텍스트가 파기됩니다.
이제 실행컨텍스트가 무엇인지, 스코프와 어떤 연관이 있는지 알게 되었습니다. 이를 바탕으로 다음 글에서는 클로저에 대해 알아보도록 하겠습니다.
'Javascript' 카테고리의 다른 글
[Javascript] 자바스크립트에서 메모리 누수의 4가지 형태 (0) | 2020.10.21 |
---|---|
[Javascript] Closure (0) | 2020.10.21 |
[Javascript] Scope (0) | 2020.10.21 |
[ES6+] var vs let vs const (0) | 2020.10.21 |
[requireJS] 전역 변수 오염을 어떻게 방지할까? (0) | 2020.10.21 |
댓글