원문 출처 : ECMA-262-3 in detail. Chapter 1. Execution Contexts by Dmitry Soshnikov
 
 
 
소개


 
이 글은 ECMAScript의 실행 콘텍스트(execution contexts), 그리고 실행 콘텍스트와 관련있는 실행 코드(Executable code)에 대해서 이야기 한다.
 
 
정의


 
ECMAScript의 실행 코드로 제어가 전달되면, 이 제어는 실행 콘텍스트으로 진입한다.
 

실행 콘텍스트(Execution context, 이하 EC)는 실행 가능한 코드의 유형을 나누고 구별하기 위해서, ECMA-262 스펙이 정의하고 있는 추상적인 개념이다.

 
표준은 기술적인 관점에서 정확한 실행 콘텍스트의 구조와 종류를 정의하고 있지는 않다. 구체적인 내용은 표준 스펙을 구현하는 ECMAScript 엔진이 처리해야 할 문제이다.
 
활성화된 실행 콘텍스트들은 논리적으로 스택의 형태를 구성하고 있다.  스택의 바닥(bottom)에는 항상 전역 문맥(global context)이 위치하고, 꼭대기(top)에는 현재(활성화되어 있는)의 실행 콘텍스트가 자리를 잡는다. 스택은 다양한 종류의 실행 콘텍스트가 들어오고 나가면서 변경(pushed/popped)된다.
 
[역주]
“context”는 “문맥”이라고 번역되기도 하는데, 기술적으로 쓰인 “context”의 의미를 “문맥”이 라는 단어가 담기에는 부족하다 판단하여, 이 글에서는 context를 번역하지 않고 발음 그대로 “콘텍스트”로 표기하고 있다.
 
 
실행 코드(Executable code)의 종류


 
추상적인 개념인 실행 콘텍스트는, 실행 코드라는 개념과 관련이 있다. 즉, 코드의 종류는 특정 순간의 실행 콘텍스트를 의미한다고 이야기할 수 있다.
 
실행 콘텍스트 스택을 배열로 정의하고 예를 들어보자.

ECStack = [];

 
제어가 함수로 들어갈 때마다(심지어 함수가 재귀적으로 호출이 되거나, 생성자로써 호출이 되어도) 실행 콘텍스트가 스택으로 들어가며, 내장되어 있는 eva l 함수의 경우도 또한 마찬가지다.
 
 
[역주]
ECMA-262는 실행 코드를 아래의 3가지 종류로 정의하고 있다.

  • 전역 코드(Global code)
  • 함수 코드(Function code)
  • eval 코드(eval code)

 
제어가 실행 코드를 만나면 실행 콘텍스트가 생성이 되고, 이 실행 콘텍스트로 제어가 이동한다. 그리고 그 안에서 실행 코드가 처리되는데, 이때 실행 코드의 종류에 따라서 실행 콘텍스트의 초기화 과정이 달라진다.
결국 실행 코드는 하나의 실행 콘텍스트에서 처리되는 코드의 단위라고 볼 수 있으며, 실행 코드의 종류에 따라서 실행 콘텍스트의 성격이 결정된다고 볼 수 있다.
 
 
 
전역 코드(Global code)


 
전역 코드는 프로그램 수준에서 실행된다. 로딩된 외부 .js 파일이나 지역 인라인 코드(<script></script> 태그의 코드 안쪽에서)가 이에 해당한다. 전역 코드는 함수 몸체 안의 어떠한 코드도 포함하지 않는다.
 
초기화 단계에서(프로그램 시작시), ECStack은 아래와 같다.

ECStack = [
  globalContext
];

 
 
[역주]
전역 코드는 전역 문맥(Global context)에서 처리되는데, 이 전역에 있는 함수의 몸체 안의 내용은 전역 코드에 포함되지 않는다. 함수 몸체의 내용은 함수 코드에 포함되며, 해당 함수가 호출 될 때 생성되는 함수 실행 콘텍스트(Function execution context)에서 처리된다.
 
 
함수 코드(Function code)


 
함수 코드로 들어갈 때(모든 종류의 함수를 이야기한다.), ECStack에는 새로운 원소(실행 콘텍스트)가 들어간다. 여기에서 이야기하는 구상 함수의 코드가 내부 함수의 코드를 포함하지 않는 다는 것을 반드시 알아야 한다.
 
 
[역주]
함수 호출 구문을 만나게 되면 ECStack에 새로운 실행 콘텍스트가 생성되어 들어가고 호출된 함수 몸체의 코드가 생성된 실행 콘텍스트 안으로 들어간다. 이때 호출된 함수의 코드에 중첩된 함수는 포함되지 않는다.
 
 
한 번 재귀적으로 자신을 호출하는 함수를 예로 들어보자.

(function foo(flag) {
  if (flag) {
    return;
  }
  foo(true);
})(false);

 
그러면, ECStack은 아래처럼 변경된다.

// foo의 첫번째 활성화
ECStack = [
  <foo> functionContext
  globalContext
];
// foo의 재귀적인 활성화
ECStack = [
  <foo> functionContext – recursively
  <foo> functionContext
  globalContext
];

 
매번 함수가 종료되면 현재 실행 콘텍스트은 제어를 반환하고, 이에 따라서 ECStack에서 제거된다 연속해서 위에서 아래로, 일반적인 스택의 동작방식을 따라서 진행된다. 이 코드의 작업이 끝난 후에 ECStack은 다시 프로그램이 끝날 때까지 오직 globalContext만을 갖게 된다.
 
throw 되었으나  catch 되지 않은 예외(exception)는 하나 이상의 실행 콘텍스트를 벗어날 수도 있다.

(function foo() {
  (function bar() {
    throw 'Exit from bar and foo contexts';
  })();
})();

 
 
Eval 코드(Eval code)


 
eval 코드는 더욱 흥미롭다. 이 경우에, 호출 문맥(calling context)이라는 개념이 있다. 이것은 eval 함수가 호출된 문맥을 가리킨다.
 
eval에 의해서 만들어진 변수나 함수 정의와 같은 행동은 정확하게 호출 문맥에 영향을 준다

// 전역 문맥에 영향을 준다.
eval('var x = 10');
(function foo() {
  // 그리고 여기의 변수 y는 foo 함수의 지역 문맥에 만들어진다.
  eval('var y = 20');
})();
alert(x); // 10
alert(y); // "y" is not defined

 

참고. ES5의 strict-mode에서 eval은 이미 호출 문맥에 영향을 주지 못하지만, 대신에 코드를 지역 샌드박스(local sandbox)에서 평가한다.

 
위의 예제에서 ECStack은 아래와 같이 변경된다.

ECStack = [
  globalContext
];
// eval('var x = 10');
ECStack.push({
  context: evalContext,
  callingContext: globalContext
});
// eval exited context
ECStack.pop();
// foo funciton call
ECStack.push(<foo> functionContext);
// eval('var y = 20');
ECStack.push({
  context: evalContext,
  callingContext: <foo> functionContext
});
// return from eval
ECStack.pop();
// return from foo
ECStack.pop();

 
아주 간단하고 논리적인 호출 스택(call-stack)이다.
 
 
결론


 
지금까지의 설명한 내용은 최소한의 이론으로 변수 객체(variable object), 스코프 체인(scope chain)과 같은 실행 콘텍스트의 세부 사항을 더 깊이 분석할 필요가 있다. 이러한 내용은 뒤의 적절한 챕터에서 다루고 있다.
 
 
추가 문헌


 
ECMA-262-3 스펙에서 해당하는 내용 – 10. Execution Contexts
 
 
 


개발왕 김코딩

Howdy. Why so serious?

0개의 댓글

답글 남기기

아바타 플레이스홀더

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다