24 Apr 2017

자바스크립트 실행 문맥(Execution Context)의 기본동작

비교적 최근에 “인사이드 자바스크립트”를 읽고 this 변수, 클로저, 실행 문맥을 이해하게 되었습니다. 자바스크립트를 즐겨 사용하지 않았지만 웹 개발을 하면서 해당 부분에 대한 이해가 필요했습니다. 이 3가지 개념은 서로 얽혀 있으며 스크립트가 실행에 필요한 중요한 요소입니다. 다른 사람의 코드를 볼일이 있거나 직접 로직을 작성할 일이 있다면 이에 대한 이해가 필요합니다. 이 글은 실행 문맥(Execution Context)의 기본 동작에 대한 설명입니다.

기본적으로 브라우저는 서버로부터 스크립트를 다운로드하여 파싱되고 실행합니다. 로딩 이벤트와 같은 HTML 이벤트에 따라 이벤트 핸들러를 등록할 수 있으며 자바스크립트 언어로 프로그래밍 할 수 있습니다.

실행문맥

실행 문맥(Execution Context)은 코드를 실행하는 과정을 추적하는 하나의 내부적인 장치를 뜻합니다. 실행 문맥은 내부에는 실제 블록안에서 사용되는 변수들을 저장하고 관리합니다. 이때 실행 문맥 내부에서 이러한 변수를 저장하는 객체를 활성 객체(Activation Object) 또는 변수 객체(Variable Object)라 부릅니다.

실행 문맥의 기본 동작을 이해하게 되면 일반적인 함수나 클로저(Clousure)에서 현재 스코프(Scope) 밖의 변수를 참조하려고 할때 어떤 변수가 어떻게 참조되는지 정확하게 알 수 있습니다. 이를 식별자 해석(Identifier Resolution)이라 합니다.

실행 문맥은 eval(…) 호출, 함수 호출, 전역 코드 실행시 새롭게 생성됩니다.

아래의 예시는 변수 객체(Variable Object)가 무엇을 저장하는지 보여줍니다.

var add = function(a, b){
    var result = a + b;
    function dummy(){}
    return result;
};

add(1,2);

// 위 함수가 실행 될때 생성되는 실행 문맥의 변수 객체(Variable Object)의 상태는 다음과 같습니다.

activationObjectForAdd = {
    arguments : [a, b],
    scopeChain : [activationObjectForGlobal,activationObjectForAdd],
    a : 1,
    b : 2,
    result : undefined,
    dummy : function(){}
};

실행 문맥은 전통적인 언어에서 사용되는 콜 스택과 역할이 유사하다고 할 수 있습니다. 하지만 콜 스택과 자바스크립트의 실행 문맥은 동작하는 방식 자체가 미묘하게 다릅니다.

전통적으로 함수를 호출할때 필요한 정보는 콜 스택에 쌓이게 되며 그 정보는 매개변수(Parameter), 종료 후 복귀 주소(Pointer), 지역 변수등 입니다. 실행 문맥도 비슷한 구조를 갖고 있습니다. 하지만 추가적으로 리스트 형태의 스코프 체인(Scope Chain)을 저장하고 있습니다. 스코프 체인은 실행 문맥들이 연결된 리스트이며 현재 실행 문맥에서 쉽게 다른 문맥의 변수를 접근하도록 도와줍니다.

함수가 정의될때 함수는 [[scope]] 프로퍼티로 현재 실행 문맥의 스코프 체인을 저장하게 됩니다. 이 스코프 체인은 추후 이 함수가 실행될때 생성되는 실행 문맥의 변수 객체와 결합되어 새로운 스코프 체인을 생성합니다. 함수가 호출된 이후 스코프 체인의 생성 과정은 다음과 같습니다.

  • 새로운 실행 문맥의 스코프 체인 = 함수 객체의 스코프 프로퍼티([[scope]] 프로퍼티) + 새로운 실행 문맥의 변수객체

이후 다른 문맥의 식별자를 찾을때 스코프 체인의 변수객체를 순차적으로 방문하면서 [[HasProperty]]을 이용해 식별자가 존재하는지 확인하게 됩니다.

실행 문맥 중 전역코드에서 최초로 생성되는 문맥은 전역 실행 문맥입니다. 스크립트의 가장 바깥쪽에 위치한 코드가 실행될때의 실행 문맥입니다. HTML DOM 모델에서는 window 프로퍼티가 전역 객체(Global Object)이면서 전역 실행 문맥의 변수 객체(Variable Object)가 됩니다. 이때 전역 실행 문맥의 스코프 체인은 자신의 변수 객체만 갖습니다.

당연하게도 어떤 함수든 생성될때 스코프 체인에 전역 실행 문맥의 변수 객체(window로 참조 가능)를 저장하게 됩니다. 따라서 어디서든 전역 변수를 사용할 수 있습니다.

var 키워드

var는 지역변수를 선언하기 위해 사용되는 키워드입니다. 당연히 함수의 실행 문맥상에 저장됩니다. 위에서 언급한 것 처럼 함수 호출 시에만 실행 문맥이 생성되다보니, 함수 안의 제어문은 별도의 문맥을 가지고 있지 않습니다. 적어도 ECMA-262 기준으로는 그런 상황이였습니다. 따라서 var 변수는 실행된 함수의 body 안의 어떤 위치에서도 참조될 수 있으며, for 문이나 if 문은 우리가 기대한 것 처럼 동작하지 않을 것 입니다.

function testEc(){
  if(true){
   var i = 11; 
  }
  console.log(i); // Can it work successfully?

}
testEc();

function testEc2(){
  for(var i=0;i<10;i++){
   for(var i=0;i<10;i++){
     console.log(i); // Can it work successfully?

   }
  console.log(i);
  }
}
testEc2();

이러한 var의 특이한 동작, 특징을 var 스코핑, 또는 함수 스코핑이라 불립니다.

참조


Tags:
0 comments