1. 스코프(Scope)란?
Scope
를 직역하면 범위, 영역 이라는 뜻입니다. 즉 자바스크립트에서의 스코프는 식별자(변수, 매개변수, 함수)가 유효한 범위 를 말합니다.
스코프(유효범위)는 함수, 변수 등이 선언되는 환경을 기반으로 합니다.
var x = 'global';
function foo() {
var x = 'local';
console.log(x)
}
foo();
console.log(x);
-
x
라는 변수를 전역환경과 함수내부에서 각각 선언을 해주고 다른 값을 할당해 주었다. - 자바스크립트는 어떤 변수를 참조할까?
- 각각 전역 스코프 와 foo 함수 스코프 를 가짐으로 아래의 결과가 출력
// local
// global
그러므로 스코프 란 식별자를 검색할 때 검색 가능한 범위 라고도 이해할 수 있으며 해당 변수가 어디에서 실행이 되며 주변에 어떤 코드들이 존재하는지에 따라 다른 결과를 출력합니다.
1-1. 스코프(Scope)는 왜 존재하는가?
- 스코프가 존재하지 않는다면 변수명이 같은 변수가 2개 이상 존재하는 경우 충돌을 일으키므로 동일한 변수명을 프로그램 전체에서 하나밖에 사용할 수 없다.
- 즉 식별자는 고유한 이름으로써 어떤 값을 구별할 수 있어야 하지만 식별자의 기능을 상실하게 되는 것이다.
2. 스코프의 종류
코드는 전역(global)
과 지역(local)
으로 나눌 수 있다.
전역스코프, 전역변수, 지역스코프, 지역변수가 존재한다.
2-1. 전역(global)의 특징
- 가장 최상위 스코프를 이야기 한다.
- 전역변수는 어디서든지 참조가 가능하다.(로컬 스코프에서도 가능)
2-2. 지역(local)의 특징
- 지역이란 함수 몸체 내부 를 말합니다.
- 지역변수는 자신이 선언된 스코프와 하위 스코프(중첩, 내부 함수)에서만 참조가 가능하다.
- 일반적으로 글로벌 스코프 에서는 로컬 스코프 의 로컬변수를 참조할 수 없습니다.
3. 스코프 체인
함수는 전역환경에서 정의가 될 수도 있지만 함수 내부에서 정의가 될 수도 있다. 이를 함수의 중첩 이라고 한다.
이렇게 중첩된 함수를 중첩함수
라고 하고 이를 포함하는 함수를 외부함수
라고 한다.
함수를 중첩할 수 있다는 것은 스코프도 중첩이 가능하다는 것다. 즉 스코프는 함수의 중첩에 의해 계층적인 구조 를 갖는다.
스코프체인의 구조
(하위) 내부함수 스코프 -> 외부함수 스코프 -> 전역스코프 (상위)`
이처럼 모든 스코프들은 하나의 계층적 구조로 연결되어있다.
이렇게 모든 스코프들이 연결된 것을 스코프 체인 이라고 명칭한다.
변수를 참조할 때 자바스크립트 엔진은 변수를 참조하는 스코프에서 시작하여 상위 스코프로 선언된 변수를 검색한다.
즉 상위스코프에서 생성된 변수는 하위스코프에서 참조가 가능하다.
스코프체인은 물리적으로 존재하며, 자바스크립트 엔진은 코드를 실행함에 앞서 자료구조인 렉시컬 환경(Lexical Environment)을 실제로 생성한다.
렉시컬환경
- 렉시컬 환경은 명세서에만 존재한다.
- 자바스크립트가 어떻게 동작하는지 설명하는데 쓰이는 이론상의 객체
- 코드를 사용해 직접 렉시컬 환경을 얻거나 조작하는 것은 불가능
함수 선언문의 렉시컬 환경
// 렉시컬 환경
실행컨텍스트 : {
렉시컬 환경 컴포넌트: {
렉시컬 환경 {
환경 레코드: {
// 모든 지역변수를 프로퍼티 {key, value}로 저장한 객체이다.
}
},
외부 렉시컬 환경 참조 : 상위 스코프 참조
// 글로벌 스코프에서는 상위 스코프가 없으므로 null을 참조
},
변수 환경 컴포넌트 : {
// 렉시컬 환경을 복사한 것으로 초기값이 동일하다 , 실행단계에서 참조하는 것은 환경레코드이다.
// 전역 렉시컬환경의 경우 null을 참조한다.
}
}
// outerFunc() {
innerFunc() {
var x = 10;
}
}
- 변수선언이 실행되면 변수 식별자가 렉시컬 환경레코드에 키(key)로 등록되고
- 변수할당이 일어나면 렉시컬 환경레코드의 변수 식별자에 해당하는 값을 반환한다.
즉 스코프체인은 렉시컬환경을 단반향으로 연결되어 있는 것이다. 만일 양방향으로 참조가 가능하다면 무한 탐색이 이어질 것이다..
3-1. 스코프체인에 의한 함수 검색
test
라는 함수선언문이 전역과 지역의 중첩함수로 동시에 존재하는 경우 런타임 이전에 함수객체가 먼저 생성된다.
즉 동일한 식별자를 가진 함수가 선언된다면 스코프 환경에 따라 식별자결정을 통해 적합한 함수를 호출한다.
// 전역함수
function test() {
console.log('global')
}
function test2() {
// 중첩 함수
function test() {
console.log('local')
}
test()
}
// 함수 호출
test2() // -> local
04. 함수 레벨 스코프
지역은 함수 내부를 말하고 지역은 지역스코프를 생성한다. 즉 지역스코프는 함수에 의해서만 생성 이 된다.var
키워드로 선언된 변수는 let, const
와 달리 함수 내부몸체(코드블록)만을 지역 스코프로 인정 한다.
이를 함수 레벨 스코프 라고 한다.
즉 var
키워드로 생성된 변수가 if문 안에 동시에 존재할 경우 if문은 함수가 아님으로 함수레벨스코프를 생성하지 않습니다. 그럼으로 모두 동일한 전역변수가 되어버리고 변수쉐도잉(재할당)이 일어납니다.
// 전역변수 x
var x = 3;
if (true) {
// 전역변수 x 중첩 변수값이 재할당(변수쉐도잉)된다.
var x = 10;
}
console.log(x) // 10
예제 2) var
키워드를 사용하여 함수 레벨 스코프 만을 지원하는 경우
var i = 10;
for (var i = 0; i < 5; i++) {
console.log(i) // 0 1 2 3 4
}
console.log(i) // 5
for문은 함수가 아님으로 함수 레벨 스코프 를 생성하지 않기 때문에 i
는 전역변수로 선언이 된다.
그럼으로 모두 같은 전역변수i
를 출력하고 있다.
예제 3) var
가 아닌 let
키워드를 사용하여 블록 레벨 스코프 를 지원하는 경우
let i = 10;
for (let i = 0; i < 5; i++) {
console.log(i); // 0 1 2 3 4
}
console.log(i) // 10
var
키워드를 사용하여 전역변수로 선언되는 경우와 다르게 let
의 경우 i
변수를 블록레벨스코프를 통해 지역변수로 선언해주기 때문에 i
값이 다르게 출력이 되는 것을 볼 수 있다.
05. 렉시컬 스코프(정적스코프)
var x = 1;
function foo() {
var x = 10;
bar()
}
function bar() {
console.log(x)
}
foo()
bar()
이 경우 bar의 상위 스코프는 두 가지의 경우에 따라 바뀔 수 있다고 예측할 수 있다.
- 동적스코프(dynamic scope) : 함수의
호출
환경에 따라서 상위 스코프를 결정 - 정적, 렉시컬스코프(laxical scope) : 함수의
선언
환경에 따라서 상위 스코프를 결정
1번
의 경우에는 bar의 상위 스코프는 foo가 될것이고 2번
의 경우에는 bar의 상위스코프는 전역스코프가 될 것이다.
실제로 코드를 작성하고 실행을 하게 되면 모두 1
을 출력하는 것을 알 수 있다.
- 자바스크립트를 포함한 대부분의 프로그래밍 언어에서는 렉시컬스코프 를 따른다.
- 즉 함수의 상위스코프는 언제나 자신이 선언된 환경의 스코프 이다.
- 함수가 정의,선언이 되면 상위 스코프의 참조를 기억하고 있다.
- 렉시컬스코프 는 클로저 와 깊은 관계가 있다.
클로저와 실행컨텍스트에 관해서는 다른 포스트에서 작성하겠습니다.
<Resource>
모던 자바스크립트 DeepDive : 스코프
'JavaScript > 실행컨텍스트' 카테고리의 다른 글
[JavaScript] 실행컨텍스트(Excution Context), 스코프체인, Variable Object (0) | 2021.11.14 |
---|