스코프는 식별자에 대한 유효 범위입니다. 함수 내부에서 선언한 변수는 함수 내부에서만 사용 가능합니다. 글로벌 변수와 지역 변수에 대해 공부하면서 이 개념에 대해 얼핏 감을 잡아본 경험이 있을 것입니다. 이런 스코프 개념은 JS뿐만 아니라 python, C 등 거의 모든 언어에서 해당되는 개념입니다.
이러한 스코프를 안에서 부터 바깥으로 차례대로 검색해 나가는 것을 '스코프 체인'이라고 합니다. 이를 가능케 하는 것이 LexiclaEnvironment의 두 번째 수집 자료인 outerEnvironmentReference입니다. outerEnvironmentReference은 현재 호출된 함수가 선언될 당시의 LexiclaEnvironment를 참조합니다. 앞서 실행 컨텍스트에서 살펴보았듯 LexiclaEnvironment가 만들어지는 것은 어떤 실행 컨텍스트가 활성화된 상태일 뿐입니다.
말이 좀 어려운데 차례대로 정리하면 이렇습니다.
a 함수 내부에 b 함수를 선언하고 b 함수 내에 c 함수를 선언한 경우
- 함수 c의 outerEnvironmentReference는 함수 b의 LexiclaEnvironment를 참조합니다.
- 함수 b의 outerEnvironmentReference는 함수 a의 LexiclaEnvironment를 참조합니다.
- 처음 선언된 함수 a의 outerEnvironmentReference는 전역 객체의 LexiclaEnvironment를 참조합니다.
이러한 구조는 연결 리스트(linked list)의 자료 구조형임을 알 수 있습니다. 따라서 가장 가까운 요소부터 차례대로만 접근할 수 있고 다른 순서로 접근하는 것은 불가능합니다. 따라서 동일한 식별자를 선언한 경우에는 스코프 체인 상에서 가장 먼저 발견된 식별자에게만 접근 가능합니다.
var test = 1;
var a = function() {
var b = function() {
var c = function() {
console.log(test) // undefined
var test = 3;
};
c();
console.log(test) // 5 // b 스코프 내부에는 test가 없으므로 가까운 a 스코프 값을 참고합니다.
};
var test = 5
b();
console.log(test) // 5 // a 스코프의 가장 가까운 스코프는 자기 자신인 5입니다.
};
a();
console.log(test); // 1 // 전역 스코프에서 가장 가까운 스코프는 자기 자신인 1입니다.
위와 같은 예시를 통해 스코프 체인을 이해해보도록 합시다.
- 먼저 시작하면 전역 컨텍스트가 활성화됩니다. 상위 컨텍스트가 없으므로 outerEnvironmentReference에는 아무것도 담기지 않습니다.
- 첫 줄, var test = 1;에서는 식별자 test에 값 1을 할당합니다.
- 다음 줄에서는 함수 a가 선언되고 a();를 만나면서 전역 컨텍스트가 중단되고 함수 a 컨텍스트가 실행됩니다.
- 함수 a 컨텍스트가 실행되면서 outerEnvironmentReference에 전역 컨텍스트의 LexiclaEnvironment가 담깁니다.
- 함수 a의 명령을 차례대로 실행합니다. 위 코드에서는 함수 b를 선언하고 그 안에 함수 c를 선언했습니다.
- b();를 만나면서 함수 a 컨텍스트가 중단되고 함수 b 컨텍스트가 실행됩니다. 역시나 outerEnvironmentReference에 a 컨텍스트의 LexiclaEnvironment가 할당됩니다.
- c();를 만나면서 b 컨텍스트가 중단되고 c 컨텍스트가 실행됩니다.
- console.log(test)를 출력합니다. 변수는 선언문만 hoisting되므로 값이 undefined가 할당됩니다.
- test에 3이 할당되었으나 해당 스코프 내에서는 활용될 곳이 없습니다.
- c(); 다음에 b 컨텍스트에서 실행되는 console.log(test)은 b 스코프에서 실행됩니다. b 스코프에는 test 변수에 할당된 값이 없으므로 가장 가까운 a 스코프에서 값을 찾기 시작합니다. 찾아보니 5가 있군요.
- b(); 다음에 a 컨텍스트에서 실행되는 console.log(test)에서 가장 가까운 스코프는 자기 자신입니다. 때문에 5입니다.
- a(); 다음에 전역 컨텍스트에서 실행되는 console.log(test)에서 가장 가까운 스코프는 자기 자신입니다. 때문에 1입니다.
이렇게 순차적으로 변수에 할당된 값이 없으면 가까운 스코프부터 확장해 나가면서 값을 찾습니다.
주의할 점은 스코프 체인에서 밖으로 스코프를 늘려나갈 수는 있지만 내부로 돌아갈 수는 없다는 것입니다. 위의 코드를 예로 들자면 b 스코프에서 내부의 c 스코프로 들어갈 수는 없지만 바깥 스코프인 a 스코프로 확장(?) 될 수는 있습니다.
🎈변수 은닉화
앞서 a 스코프에서 전역 변수와 이름이 같은 test를 선언한 바 있습니다. 이렇게 되면 a 스코프 이하에 있는 스코프들은 전역 변수에 접근할 수 없게 됩니다. 이러한 기법을 '변수 은닉화'라고 합니다.
'Programming Language > 🟨 Javascript (Core)' 카테고리의 다른 글
this(2) : 명시적으로 this를 바인딩하기 (0) | 2020.04.15 |
---|---|
this(1) : this에 대한 모든 것 (0) | 2020.04.15 |
실행 컨텍스트(1) : 실행 컨텍스트와 호이스팅 (0) | 2020.04.14 |
데이터 타입, immutable와 deep Copy, freeze, 메모리 (0) | 2020.04.14 |
코어 자바스크립트 : 소개 (0) | 2020.04.14 |