본문은 JavaScript 언어에 대한 이해를 높이기 위해 "You Don't Know JS(카일 심슨 저)"를 읽고 공부한 내용을 정리한 글입니다. 공부가 목적이기 때문에 필요에 따라 생략이나 수정, 추가된 부분이 있을 수 있습니다.
클로저란?
MDN은 다음과 같이 클로저를 정의하고 있다.
클로저는 함수와 함수가 선언된 어휘적 환경(렉시컬 환경)의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
클로저는 자바스크립트 고유의 개념이 아니라 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어(Functional Programming language: 얼랭(Erlnag), 스칼라(Scala), 하스켈(Haskell), 리스프(Lisp)…)에서 사용되는 중요한 특성이다. 책의 저자는 클로저에 대해서 어렵게 생각하는 것이이 아니라, 그저 당연한 것으로 받아들이길 강조하고 있다.
클로저란?
클로저란 함수가 속한 렉시컬 스코프를 기억하여 함수가 렉시컬 스코프 밖에서 실행될 때에도 이 스코프에 접근할 수 있게 하는 기능이다.
function foo() {
var a = 2;
function bar() {
console.log(a);
}
return bar;
}
var baz = foo();
baz(); // 2, 이것이 클로저다!
함수 bar( )는 foo( )의 렉시컬 스코프의 접근할 수 있고, 함수 foo( )는 bar( ) 함수 자체를 값으로 넘긴다. 즉, bar를 참조하는 함수 객체 자체를 반환한다.
foo( )를 실행하여 반환한 값(bar( ) 함수)를 baz라 불리는 변수에 대입하고 실제로는 baz( ) 함수를 호출했다. 이는 다른 식별자를 통한 참조로서, 내부 함수인 bar( )를 호출한 것과 동일하다. bar( )는 분명히 실행됐지만, 함수가 선언된 렉시컬 스코프 밖에서 실행됐다는 점이 중요하다.
일반적으로 foo( )가 실행된 후에는 가비지 콜렉터를 통해 foo( )의 내부 스코프가 사라졌다고 생각할 수 있지만, 사실은 그렇지 않다. 클로저가 작동한 까닭이다. foo의 내부 스코프는 bar( ) 함수가 사용중이기 때문에 해제되지 않는다. 선언된 위치 덕에 bar( )는 foo( ) 스코프에 대한 렉시컬 스코프 클로저를 가지고, foo( )는 bar( )가 나중에 참조할 수 있도록 스코프를 살려둔다. 즉, bar( )는 여전히 해당 스코프에 대한 참조를 가지는데, 그 참조를 바로 클로저라고 한다.
스코프에 대한 참조
스코프에 대한 참조라는 말이 다소 낯설 수 있어 간략하게만 소개하자면, 실행 컨텍스트는 변수 객체(Variable Object), 스코프 체인(Scope Chain), this(this value)라는 3가지 프로퍼티를 지닌다. 이 중 스코프 체인은 일종의 리스트로서 전역 객체와 중첩된 함수의 스코프의 참조를 아래의 그림과 같이 차례로 저장하고 있다. 이러한 맥락에서 '스코프에 대한 참조'라는 말이 등장한 것이다.
좀 더 자세한 내용은 Execution Context 문서를 참고하자.
또 다른 예시들을 마저 살펴보자. 아래의 코드와 다음과 같이 함수를 값으로 넘겨 다른 위치에서 호출하는 행위는 모두 클로저가 작용한 예다. foo( )의 내부 스코프에 대한 fn의 클로저는 변수 a에 접근할 때 확인할 수 있다.
function foo() {
var a = 2;
function baz() {
console.log(a); // 2
}
bar(baz);
}
function bar(fn) {
fn(); // 클로저 작용!
}
foo();
var fn;
function foo() {
var a = 2;
function baz() {
console.log(a); // 2
}
fn = baz; // baz 함수를 전역 변수에 할당
}
function bar() {
fn(); // 클로저 작용!
}
foo();
bar(); // 2
위의 두 코드를 통해 알 수 있는 건, 어떤 방식을 사용하여 내부 함수를 자신이 속한 렉시컬 스코프 밖으로 수송하는지와 관계없이 함수는 처음 선언된 곳의 스코프에 대한 참조를 유지한다는 것이다. 즉, 어디에서 해당 함수를 실행하든 클로저가 작용한다고 할 수 있다.
결론적으로 좀 더 쉽게 클로저를 설명하자면 다음과 같이 설명할 수 있을 것이다.
클로저란 함수가 자신이 생성될 때의 환경, 즉 렉시컬 환경을 기억하는 기능이다.
간단하게 클로저를 설명했지만, 자세한 동작원리와 활용이 궁금하다면 아래 항목의 참고 문서를 살펴보도록 하자.
2. 참고
'프로그래밍 언어 > JavaScript + TypeScript' 카테고리의 다른 글
[You Don't Know JS] 6. 호이스팅(Hoisting) (0) | 2021.07.03 |
---|---|
[You Don't Know JS] 5. 스코프(Scope) (0) | 2021.07.02 |
[You Don't Know JS] 4. 문법 (0) | 2021.06.29 |
[You Don't Know JS] 3. 네이티브(Native) (0) | 2021.06.24 |
[스크랩] JavaScript(ES6), TypeScript, Node.js 개발자를 위한 무료 e북 4가지 (0) | 2021.06.22 |