자바스크립트도 다른 언어와 마찬가지로 스코프, 즉 유효 범위가 있다.

자바스크립트에서는 for() {}, if {}와 같은 구문은 유효 범위가 없다.

오직 함수만이 유효 범위의 한 단위가 된다.


유효 범위를 나타내는 스코프가 [[Scope]] 프로퍼티로 각 함수 객체 내에서 연결리스트 형식으로 관리되는데 이를 "스코프 체인" 이라고 한다.


각의 함수는 [[scope]] 프로퍼티로 자신이 생성된 실행 컨텍스트의 스코프 체인을 참조한다.

함수가 실행되는 순간 실행 컨텍스트가 만들어지고, 이 실행 컨텍스트는 실행된 함수의 [[scope]] 프로퍼티를 기반으로 새로운 스코프 체인을 만든다. 함수의 실행이 완료되면 실행 컨텍스트는 사라진다.

함수가 생성될때 스코프 생성 -> 함수가 실행될때 컨텍스트 생성(이때 생성된 스코프를 참조)



1. 전역 실행 컨텍스트의 스코프 체인

1
2
3
4
var var1 = 1;
var var2 = 2;
console.log(var1);  // 1
console.log(var2);  // 2
cs

위 예제에는 현재 전역 실행 컨텍스트 단 하나만 실행되고 있어 참조할 상위 컨텍스트가 없다.

자신이 최상위에 위치하는 변수 객체인 것이다.

따라서 이 변수 객체의 스코프 체인은 자기 자신만을 가진다.

다시 말해서, 변수 객체의 [[scope]]는 변수 객체 자신을 가리킨다.

이 변수 객체가 곧 전역 객체가 되는 것이다.




2. 함수를 호출한 경우 생성되는 실행 컨텍스트의 스코프 체인

1
2
3
4
5
6
7
8
9
10
11
var var1 = 1;
var var2 = 2;
function func() {
    var var1 = 10;
    var var2 = 20;
    console.log(var1); // 10
    console.log(var2); // 20
}
func();
console.log(var1);  // 1
console.log(var2);  // 2
cs

함수 객체가 생성될 때, 그 함수 객체의 [[scope]]는 

현재 실행되는 컨텍스트의 변수 객체에 있는 [[scope]]를 그대로 가진다.

따라서, func 함수 객체의 [[scope]]는 전역 변수 객체가 된다.


func() 함수를 실행하면 새로운 컨텍스트가 만들어진다.

func() 함수 컨텍스트의 스코프 체인은 실행된 함수의 [[scope]] 프로퍼티를 그대로 복사한 후,

현재 생성된 변수 객체를 복사한 스코프 체인의 맨 앞에 추가한다.


위 과정을 정리해보자.

1) 각 함수 객체는 [[scope]] 프로퍼티로 현재 컨텍스트의 스코프 체인을 참조한다.

2) 한 함수가 실행되면 새로운 실행 컨텍스트가 만들어지는데, 이 새로운 실행 컨텍스트는 자신이 사용할 스코프 체인을 다음과 같은 방법으로 만든다.

 - 자신을 생성한 함수 객체의 [[scope]] 프로퍼티를 복사하고, 

 - 새롭게 생성된 변수 객체를 해당 체인의 제일 앞에 추가한다.

3) 요약 하면, 스코프 체인 = 현재 실행 컨텍스트의 변수 객체 + 상위 컨텍스트의 스코프 체인



다음 두 예제를 통해 스코프 체인에 대해서 확실히 알아보자!

1
2
3
4
5
6
7
8
9
10
11
12
13
var value = "value1";
 
function printFunc(func) {
    var value = "value2";
    
    function printValue() {
        return value;
    }
    
    console.log(printValue());
}
 
printFunc();
cs

앞서 설명한 스코프 체인에 대해 제대로 이해했다면, 결과 값이 "value2"가 된다는 것을 바로 알 수 있다.


그림으로 표현하면 아래와 같다.


다음 예제의 결과는 조금 다를 것이다.

1
2
3
4
5
6
7
8
9
10
11
var value = "value1";
 
function printValue() {
    return value;
}
function printFunc(func) {
    var value = "value2";
    console.log(func());
}
 
printFunc(printValue);
cs

위 예제는 각 함수 객체가 처음 생성될 당시 실행 컨텍스트가 무엇인지를 생각해야 한다.

각 함수 객체가 처음 생성될 때 [[scope]]는 전역 객체의 [[scope]]를 참조한다.

따라서, 각 함수가 실행될 때 생성되는 실행 컨텍스트의 스코프 체인은 전역 객체와 그 앞에 새롭게

만들어진 변수 객체가 추가 된다. 결과 값으로는 "value1" 이 나올 것이다!


그림으로 표현하면 다음과 같다.



지금까지 실행 컨텍스트가 만들어지면서 스코프 체인이 어떻게 형성되는지 살펴보았다.

이렇게 만들어진 스코프 체인으로 식별자 인식(identifier resolution)이 이루어진다.

식별자와 대응되는 이름을 가진 프로퍼티가 존재하는지 확인 한다.


함수를 호출할 때 스코프 체인의 가장 앞에 있는 객체가 변수 객체이므로, 이 객체에 있는 공식 인자, 내부 함수, 지역 변수에 대응되는지 먼저 확인한다.

대응 되는 이름의 프로퍼티를 찾을 때까지 계속해서 다음 객체로 이동하며 찾는다.



스코프 체인이 생성되어도 사용자가 임의로 수정이 가능하다.

with 구문을 활용해서 표현식을 실행할 때, 표현식이 객체이면 객체는 현재 실행 컨텍스트의 스코프 체인(활성화 객체 바로 앞)에 추가된다.

with 구문은 다른 구문을 실행하고 실행 컨텍스트의 스코프 체인을 전에 있던 곳에 저장한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var y = { x:5 };
 
function withExamFunc() {
    var x = 10;
    var z;
 
    with(y) {
        z = function() {
            console.log(x);    
        }
    }
    z();
}
withExamFunc();
cs


함수 선언은 with 구문의 영향을 받지 않고 함수 객체가 생성되지만, 함수 표현식은 안에서 with 구문과 함께 실행될 수 있다.

위 코드에서는 with 구문의 실행으로 전역 변수 y에 의해 참조되는 객체를 함수 표현식이 실행되는 동안 스코프 체인의 맨 앞에 추가한다.


with 구문은 성능 이슈로 인해 자제를 해야한다~



+ Recent posts