Identifier Resolution : 식별자의 해석은 어떻게 되는가?

Resolution 이란 단어를 어떻게 해석해야할찌 한참을 고민했다. 결국은 동일한 식별자 이름들이 있을때, 이것을 어떻게 판별해서 가져오는가 하는 내용이기 때문에 대충 Resolution 이 그런 의미로 쓰였음을 알고 다음으로 넘어가자.

원문 : http://www.jibbering.com/faq/faq_notes/closures.html
원문을 A4 용지로 출력하면, 6페이지 마지막 단락에 있는 내용이다.

식별자는 스코프 체인을 통해서 판별된다. ECMA 262 는 this 라는 키워드 대신에 식별자로 목록화를 한다. 이것이 그렇게 불합리한 이야기는 아니다. 왜냐면, 실행문맥 안에서 스코프 체인을 참조하는 참조자가 없을때만 식별자는 this 값에 의해 결정되어 지기 때문이다.

식별자는 스코프 체인 안에 있는 첫번째 객체부터 찾기 시작한다. 그 첫번째 객체의 프로퍼티들의 이름과 찾고자 하는 식별자가 같은 이름인가를 확인한다. 이 스코프 체인은 만약에 객체가 프로퍼티 체인을 가지고 있다면, 그 프로퍼티 체인까지 스코프 체인으로 만들어 버린다. 그렇기 때문에 스코프 체인을 뒤진다는것은 접근할수 있는 모든 식별자들을 뒤져보는것과 마찬가지가 된다.

이런식으로 첫번째 객체를 뒤져도 찾고자 하는 식별자를 찾을수 없을때는 다음 객체를 뒤지고, 또 그다음 객체를 찾아본다. 찾고자 하는 값이 계속 없으면 최종적으로 전역객체까지 찾게된다.

역주)
실행 문맥이 새로 생성될때마다 Activaton 객체가 그 스코프 체인의 맨앞에 놓이기 때문에 함수 내에서의 지역변수가 전역변수 이름과 같을 경우 전역변수는 가려지게 되는것이다.

생각보다 심각하다…

내 목소리가 지금 심상치 않다..

오늘로써 4일째..–
어제보다 더 심해졌다.. 아니 오늘 아침보다 오늘 저녁에 더 심해졌다..
이제 대답을 하기 조차 버겁다..–
말하는게 두렵다..
내일은 양호실언니한테 가봐야겠당..
심상치 않은 조짐..
편도선염?? ㅜㅜ 그건 아닌거 같은데…
내 판단엔 과로인듯…–;.. 피로누적…

Scope chains 과 scope 프로퍼티 제대로 이해하기!!

아래 포스팅과 이어서 이번에는 Scope chains 과 [[scope]] 프로퍼티에 대해서 제대로 한번 알아보자.

원문 출처: http://www.jibbering.com/faq/faq_notes/closures.html

함수 호출을 위한 실행 문맥과 스코프 체인은 함수 객체의 [[scope]] 프로퍼티에 정의된 실행 문맥의 Activation/Variable 객체를 스코프의 맨 앞쪽에 더함으로써 생성되어진다. 그래서, 내부적으로 [[scope]] 프로퍼티가 어떻게 정의되는지 이해하는 것은 매우 중요하다.

ECMAScrpt 에서 함수(function)은 객체다. 그리고 이런 객체들은 변수 인스턴스화 과정을 거치면서 함수 선언에 의해 만들어지거나, 함수표현식을 평가할때, 혹은 함수 생성자에 의해서 invoking 을 수행할때 만들어진다.

Function 생성자와 함께 생성되는 함수(Function) 객체는 항상 전역객체를 포함하고 있는 스코프 체인(scope chain)을 가르키는 [[scope]] 프로퍼티를 가진다.

함수 선언식이나 표현식과 함께 생성되는 함수 객체는 그 실행 문맥안에서 스코프 체인을 가진다. 그리고 이런 함수 객체들은 그 실행문맥 안에서 내부적인 [[scope]] 프로퍼티를 생성하고 할당한다.

쉬운 예로 아래와 같은 전역 함수 선언식을 보자( 표현식 아님!!  표현식은 그 다음 예제..)

function exampleFunction(formalParameter) {
    ….    // function body code
}

위 함수는 전역 실행 문맥안에서 변수 인스턴스화 과정을 통해 함수 객체로 생성된다.
전역 실행 문맥은 오직 전역 객체만을 가지는 스코프 체인을 가진다. 그래서 이 함수는 “exampleFunction” 이라는 이름을 가지는 전역 객체의 프로퍼티로 생성되어 지고, 내부적으로  [[scope]] 프로퍼티가 할당되어 진다. 이 [[scope]]프로퍼티는 오직 전역 객체만을 포함하고 있는 스코프 체인을 가르키게 된다.

함수 표현식이 전역 실행 문맥 안에서 수행될때, 비슷한 스코프 체인이 할당되어진다.
다음 예제를 보자.

var exampleFuncRef = function() {
   … // function body code
}

위와 같은 경우를 제외하고 전역 객체의 이름있는 프로퍼티들은 전역 실행 문맥에서 변수 인스턴스화 과정을 거치면서 생성되어 진다. 하지만 위 예제의 함수 객체는 인스턴스화 과정에서 생성되는것이 아니라 변수에 할당된 함수 표현식이 평가 될때 생성된다. 그러므로 인스턴스화 과정을 거칠때 위 예제의 함수는 전역 객체에 의해 참조 되지 않는다.

하지만 함수 객체의 생성은 여전히 전역 실행 문맥안에서 수행 되기때문에 이 생성된 함수 객체의 [[scope]] 프로퍼티는 할당된 스코프 체인 안에서 여전히 전역 객체만을 포함하게 된다.
 
내부 함수(중첩된 함수) 선언식이나 표현식은 한 함수의 실행문맥 안에서 함수 객체가 생성되어 지는 결과를 가져온다. 그래서 보다 복잡한 스코프 체인이 되어버린다.
아래 코드를 보자.

function exampleOuterFunction (formalParameter) {
   function exampleInnerFunctionDec() {
      … // inner function body
   }
     …. // the rest of the outer function body.
}
exampleOuterFunction(5);

밖에 선언된 함수의 객체는 전역 실행 문맥의 변수 인스턴스화 과정에서 만들어지기 때문에 그 함수(exampleOuterFunction) 객체의[[scope]] 프로퍼티는 전역객체만을 가지는 스코프체인을 가르키게 된다.

전역코드가 exampleOuterFunction(5); 이 구문을 수행할때, exampleOuterFunction 함수가 호출되고 이것은 이 함수를 위한 새로운 함수 실행 문맥을 만들어주게 된다. 그리고 앞서서 설명했던것처럼, Activation 과 Variable 객체가 그 실행 문맥 안에 만들어지게 된다.

이때 새로운 실행문맥의 스코프는 새로운 Activation 객체를 포함하는 체인을 구성하게 된다. 그리고 이 체인은 Activation 객체에 이어서 호출된 본래 함수 즉, exampleOuterFunction 객체의 [[scope]] 프로퍼티를  참조하게 되기때문에, Activation객체에서 exampleOuterFunction 객체 [[scope]] 프로퍼티가 가르키는 전역 객체 까지 체인을 형성하게 되는 것이다.

새로운 실행 문맥의 변수 인스턴스화 과정을 거치게 되면서 exampleInnerFunctionDec 함수 객체가 생성이 되고, exampleInnerFunctionDec 함수 객체의 [[scope]]프로퍼티는 현재의 실행 문맥 안에서 생성된 scope를 할당받는다.
즉, exampleInnerFunctionDec 함수 객체의 [[scope]]는 현재 실행 문맥의 Activation 객체와 전역객체인 window 객체까지 체인을 형성할수 있게 된다.

지금까지 소스코드와 구조에 따라서 자동적으로 컨트롤 되는 모습을 보았다. 실행 문맥의 스코프 체인은 새로운 실행문맥에서 함수 객체의 [[scope]] 프로퍼티가 생성되고, 본래 함수 객체의  [[scope]] 프로퍼티들이 가르키는 실행 문맥의 스코프안의 객체까지 연결될수 있음을 나타낸다. 하지만 ECMAScipt 는 with문을 통해서 스코프 체인을 수정할수 있다.

with 문이 표현식을 평가했을때, 만약에 그 표현식이 객체라면, 그 객체는 현재 실행문맥의 스코프 체인안에 추가된다.(추가되는 객체는 현재 스코프 체인의 맨 앞에 있는 Activation 객체 앞에 추가가 된다.) 그리고 with 문 다음의 문장들을 수행하고, 완료한뒤에 본래의 스코프 체인을 복구한다. (아~ — 스코프 체인안에 객체에 추가하고 복구하는 과정때문에 속도가 느려지는군…–_)

with문은 변수 인스턴스화 과정중에 함수의 객체가 생성되기 때문에 함수 선언식에는 영향을 주지 않는다. 하지만 함수 표현식은 with 문에 의해서 평가 되어 질수 있다.

/* 전역 변수 y를 만들고 그것이 하나의 객체를 참조한다. */
var y = {x:5}; // x 프로퍼티를 가지는 객체 리터럴
function exampleFuncWith(){
    var z;
    /* 전역 변수에 의해 참조 되는 객체를 y에 추가하고 그것을 스코프 체인 앞에 둔다.    */
    with(y){
        /* 함수 객체를 생성하기 위해 함수식을 평가하고, 지역 변수 z를 함수 객체의 참조로 할당한다.
        */

        z = function(){
            … // inner function expression body;
        }
    }
    …
}
/* execute the – exampleFuncWith – function:- */
exampleFuncWith();

exampleFuncWith 함수가 호출되어 질때, 그 결과의 실행 문맥은 그 실행문맥 안의 Activation 객체에서  전역 변수까지 참조하는 스코프 체인을 가진다. 그리고 with문을 수행할때 전역 변수 y가 참조하는 객체를 이 스코프 체인 맨 앞에 추가한다.
즉, y객체 –> Activation 객체 –> window 객체 순으로 체인이 생긴다.

함수 표현식(z=function(){})의 의해 평가되서 생성된 함수 객체(z)의 [[scope]] 프로퍼티는 현재의 스코프를 가르키게 된다. 즉, y객체를 포함하고, 뒤이어 Activation 객체 그리고 외부함수 실행문맥이 가지는 스코프 영역의 전역객체 즉, window 객체까지 체인을 이루게 된다.

with 문 블락이 종료되면, 현재의 실행 문맥은 복구되지만,( 스코프 맨 앞에 있던 y 객체가 지워진다.) 이미 생성된 함수 객체(z)의 [[scope]] 프로퍼티는 여전히 y 객체를 가지는 스코프 체인을 가지고 있는다.