LHJ

I'm a FE developer.

7.6 함수, 클로저, 정적 스코프

05 May 2020 » js_lj

지금까지는 블록으로만 스코프를 설명했으므로 정적 스코프를 파악하기 쉬웠습니다.
블록을 들여 쓰면 정적 스코프를 파악하기가 더 쉽습니다.
하지만 함수는 여기서 정의하고 저기서 호출하는 식으로 사용하므로 스코프를 제대로 이해하려면 더 고민해야 합니다.

모든 함수를 전역에서 정의하고 함수 안에서 전역 스코프를 참조하지 않도록 신경 쓰는 전통적 프로그램에서는(필자는 이런 스타일을 권합니다) 함수가 어떤 스코프에 접근할 수 있는지 생각할 필요도 없습니다.

하지만 최신 자바스크립트에서는 함수가 필요한 곳에서 즉석으로 정의할 때가 많습니다.
함수를 변수나 객체 프로퍼티에 할당하고, 배열에 추가하고, 다른 함수에 전달하고, 함수가 함수를 반환하고, 심지어 이름조차 없을 때도 잦습니다.

함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는 경우가 많습니다.
이런 것을 보통 클로저(closure) 라고 부릅니다.
스코프를 함수 주변으로 좁히는(closure) 것이라고 생각해도 됩니다.
클로저 예제를 하나 살펴봅시다.

let globalFunc;             // 정의되지 않은 전역 함수
{
	let blockVar = 'a';     // 블록 스코프에 있는 변수
	// 함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는 경우
	globalFunc = function () {
		console.log(blockVar);
	}
}
globalFunc();               // "a"

globalFunc는 블록 안에서 값을 할당받았습니다.
이 블록 스코프와 그 부모인 전역 스코프가 클로저를 형성합니다.
globalFunc를 어디서 호출하든, 이 함수는 클로저에 들어있는 식별자에 접근할 수 있습니다.

이 예제를 잘 이해해야 합니다.
globalFunc을 호출하면, 이 함수는 스코프에서 빠져나왔음에도 불구하고 blockVar에 접근할 수 있습니다.
일반적으로 스코프에서 빠져나가면 해당 스코프에서 선언한 변수는 메모리에서 제거해도 안전합니다.
하지만 여기서는 스코프 안에서 함수를 정의했고, 해당 함수는 스코프 밖에서도 참조할 수 있으므로 자바스크립트는 스코프를 계속 유지합니다.

즉, 스코프 안에서 함수를 정의하면 해당 스코프는 더 오래 유지됩니다.
또한 일반적으로는 접근할 수 없는 것에 접근할 수 있는 효과도 있습니다.
다음 예제를 보십시오.

let f; // 정의되지 않은 함수
{
	let o = { note: 'Safe' };
	f = function() {
		return o;
	}
}
let oRef = f();
oRef.note = "Not so safe after all!";

일반적으로는 자신의 스코프에 없는 것들에는 접근할 수 없습니다.
함수를 정의해 클로저를 만들면 접근할 수 없었던 것들에 접근할 방법이 생깁니다.
이건 매우 중요한 측면입니다.
이후 장에서 다시 설명하겠습니다.