함수 레벨 vs 블록 레벨 스코프
스코프는 식별자(변수 이름, 함수 이름, 클래스 이름 등)의 유효 범위를 의미한다. 다른 코드가 해당 식별자를 참조할 수 있는 범위를 뜻한다. 스코프는 크게 전역 스코프와 지역 스코프로 구분되며, 자신이 선언된 위치에 의해 스코프가 결정된다는 특성이 있다.
함수 레벨 스코프
그렇다면 함수 레벨 스코프는 무엇일까? 이는 함수의 코드 블록만을 지역 스코프로 인정하는 것을 말한다. 함수의 코드 블록이 아닌 if, for, while 등의 코드 블럭은 지역 스코프로 인정되지 않는다. var가 바로 함수 레벨 스코프다. 함수가 아닌 코드 블록에서 var 키워드로 변수를 선언하면 지역 변수가 아니다. 전역 변수다.
// 함수 레벨 스코프
var arr = [];
for(var i = 0; i < 5; i++) { // var로 선언한 i는 전역 변수다.
arr[i] = function() {
return i;
}
}
for (const index in arr) {
console.log(arr[index]()); // 5 5 5 5 5
}
arr[index]로 함수를 호출하면 i가 반환된다. 이 i는 전역 변수 i를 가리킨다. arr[index]로 함수를 호출하는 시점에는 전역 변수 i에 5가 할당되어 있다. 따라서 5가 다섯 번 출력된다.
블록 레벨 스코프
블록 레벨 스코프는 모든 코드 블록(함수, if, for, while 등)을 지역 스코프로 인정하는 것이다. let, const가 블록 레벨 스코프다. 코드 블록 내에서 let, const로 변수를 선언하면 지역 변수다. 사실 이것이 더 자연스럽고 정상적이다.
// 블록 레벨 스코프
var arr = [];
for (let i = 0; i < 5; i++) { // let으로 선언한 i는 지역 변수다.
arr[i] = function() {
return i;
}
}
for (const index in arr) {
console.log(arr[index]()); // 0 1 2 3 4
}
arr[index]로 함수를 호출하면 i가 반환된다. 이 i는 무엇을 가리킬까? for문에서 함수가 선언되었을 당시의 지역변수 i를 가리킨다. arr[0]일 때 지역변수 i는 0, arr[1]일 때 지역변수 i는 1…과 같은 식이다. 따라서 0 1 2 3 4가 정상적으로 출력된다.
한편, 위와 같은 현상은 클로저(Closure)의 특성이기도 하다. 클로저는 내부함수가 외부함수가 소멸된 이후에도, 외부함수의 지역 변수에 접근할 수 있는 것을 의미한다. 여기서는 for 문이 외부함수의 역할을 하고 있다. for문이 종료되었으므로 지역변수 i는 소멸하였다. 그러나 여전히 내부함수를 통해 i에 접근하는 것이 가능하다. 또한 놀랍게도, 각각의 내부함수들은 자신이 선언되었을 당시의 i 값을 기억하고 있다. 그래서 0 1 2 3 4가 출력되는 것이다. 이것이 클로저의 심오한 특성이다.
클로저에 대한 글은 아니므로, 간단히만 언급하고 줄이도록 하겠다.
참고자료
“모던 자바스크립트 Deep Dive”, 이웅모, 위키북스, 2020.09.25, pp.209-211
클로저 - 생활코딩