스코프는 변수의 유효 범위를 뜻한다. 스코프(Scope)는 변수가 어디서, 어떻게 선언되었는지에 따라 그 변수의 유효한 범위가 결정되는 규칙이기도 하며, 자바스크립트에서는 이 규칙에 따라 참조의 대상이 되는 변수를 탐색해나간다.
- 전역 스코프(Global Scope): 같은 파일 내의 코드 어디에서든지 참조할 수 있다.
- 지역 스코프(Local Scope or Function-level Scope): 함수 스코프 또는 코드 블록(if-else, for, while, try-catch 등)이 만든 블록스코프로, 자기 자신 또는 그 하위 함수/블록에서만 참조할 수 있다.
자바스크립트 엔진은 변수(식별자)를 찾을 때 일단 자신이 속한 스코프에서부터 찾아보고, 그 스코프에 찾는 변수가 없으면 상위 스코프를 타고 올라가며 차례차례 찾아 나간다. 이를 스코프 체인(Scope Chain)이라고 하며, 스코프가 중첩되어있는 모든 상황에서 발생한다.
const x = 'x1';
function c(){
const y = 'y';
console.log('c');
function b(){
const z = 'z';
console.log('b');
c();
}
}
function a(){
const x = 'x2';
console.log('a');
b();
console.log(x);
}
a();
c();
위와 같은 코드에서 각 변수들의 스코프 관계를 살펴보면,
x,c,a 는 전역스코프에 해당하고
y와 b는 c 함수에서, z는 b함수, x는 a함수 스코프에 해당된다.
이 때, x라는 변수의 값을 찾고자 할 때, x는 전역스코프에도 해당하고 a함수의 스코프에도 해당된다. a함수 자체는 전역스코프에 해당하기 때문에 x는 a함수 안에서 할당된 'x2' 값이 저장된다.
클로저는 함수의 변수와 함수가 해석되는 스코프를 아울러 말한다. 이는 lexical scoping 규칙과 관련이 있다.
lexical scoping 은 함수가 정의된 시점의 스코프 체인을 사용하여 함수가 실행된다는 뜻이다.
함수와 함수 바깥의 변수에 관계에 따라서 다른 결과를 만드는 경우가 있다. 이는 let 과 var 변수에도 적용되는데,
let은 블록스코프 변수이고 var은 함수스코프 변수이다.
function wrapElements(a) {
var result = [], i, n;
for (i = 0, n = a.length; i < n; i++) {
result[i] = function () { return a[i]; }
}
return result;
}
var wrapped = wrapElements([10, 20, 30, 40, 50]);
var f = wrapped[0];
f(); // undefined
이 예제에서 i는 var로 선언되어 있다. 따라서 wrapElements 함수 내의 모든 중첩함수들은 동일한 i를 참조한다.
따라서 for문이 다 돌고 i는 5가 된 상황이고 wrapped에 할당된 어떤 함수를 호출해도, function () { return a[i] ; } 에서
a[i] 는 a[5]가 되므로 undefined 라는 값이 나온다.
이는 i를 let 블록스코프로 고정시키면 해결할 수 있다.
function wrapElements(a) {
var result = [], n;
for (let i = 0, n = a.length; i < n; i++) {
result[i] = function () { return a[i]; }
}
return result;
}
var wrapped = wrapElements([10, 20, 30, 40, 50]);
var f = wrapped[0];
f(); // 10
여기에서 for 문 안에 선언된 i는 블록 스코프로 for문 중괄호 안에 고정된다. 따라서 result 배열에 각각의 변수 a[0] ~ a[4] 가 저장되고 f를 출력하면 10이 나온다.
더 많은 예제와 자세한 설명은 https://developer-alle.tistory.com/369 에서 찾을 수 있다.