Closure (클로저) 너 뭐니?
💡 [코어자바스크립트-정재남, 위키북스]를 참고&인용하여 블로그를 작성했습니다.
클로저는 여러 함수형 프로그래밍 언어에서 등장하는 보편적인 특성이다. 자바스크립트 만의 고유한 개념이 아니라 ECMAScript 명세에서도 클로저를 따로 정의하지 않는다.
Closure의 사전적 정의
다양한 서적에서 클로저를 한 문장으로 요약📚
- 자신을 내포하는 함수의 컨텍스트에 접근할 수 있는 함수 - 더글라스 크록포트, <자바스크립트 핵심 가이드>, 한빛미디어
- 함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는 것 - 에든 브라운, <러닝 자바스크립트>, 한빛미디어
- 함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수 - 존 레식, <자바스크립트 닌자 비급>, 인사이트
- 이미 생명 주기상 끝난 외부 함수의 변수를 참조하는 함수 - 송형주 고현준, <인사이트 자바스크립트>, 한빛미디어
- 자유변수가 있는 함수와 자유변수를 알 수 있는 환경의 결합 - 에릭 프리먼, < Head First Javascript Programming>, 한빛미디어
- 로컬 변수를 참조하고 있는 함수 내의 함수 - 야마다 요시히로, <자바스크립 트 마스터북>, 제이펍
- 자신이 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수들만을 기억하여 유지시키는 함수 - 유인동, <함수형 자바스크립트 프로그래밍>, 인사이트
MDN슨상님의 설명
MDN에서는 클로저에 대해 다음과 같이 설명한다.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
클로저는 함수와 함수가 선언된 당시의 lexical environment의 조합이다. 즉, 클로저는 내부 함수가 외부 함수의 스코프에 접근할 수 있게 해주는 것을 의미한다. 자바스크립트에서 함수가 생성될 때마다 함수가 생성된 시점에 클로저가 함께 생성된다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
일단 눈으로 보고 이해해보기 👀
function outer(x) {
return function inner() {
return x
}
}
const two = outer(2)
console.log(two()) // 2
outer
함수 안에는 inner
함수가 있다. inner
함수는 outer
함수에 인자로 들어간 x
를 리턴한다.
outer
함수가 호출되면 outer
의 생명주기는 끝난다. 하지만, inner
함수는 x
를 참조한다.
위의 코드 예시처럼 외부 스코프를 기억하고 있는 것을 Closure(클로저) 라고 한다.
다 이해하지 못해도 괜찮은 조-금 어려운 버전 💆🏻♂️
const outer = () => {
let a = 0
const inner = () => {
console.log((a += 1))
}
inner()
}
outer() // 1
outer
함수 안에서 변수 a
를 선언했고, outer
함수 내부에 inner
함수에서 a
의 값을 1 증가시킨 후 출력했다. inner
함수 내부에서 a
를 선언하지 않았기 때문에 environmentRecord
에서 값을 찾지 못하므로 outerEnvironmentReference
에 지정된 상위 컨텍스트인 outer
의 LexicalEnvironment
에 접근해서 다시 a
를 찾는다. outer
함수의 실행 컨텍스트가 종료되면 LexicalEnvironment
에 저장된 식별자들(a
, inner
)에 대한 참조를 지운다. 그러면 각 주소에 저장돼 있던 값들은 자신을 참조하는 변수가 하나도 없게 되므로 가비지 컬렉터의 수집 대상이 될 것이다.
위의 내용을 조금 바꿔서 다른 예시를 살펴보자
const outer = () => {
let a = 0
const inner = () => {
return (a += 1)
}
return inner()
}
const outer2 = outer()
console.log(outer2) // 1
이번에도 inner
함수 내부에서 외부변수인 a
를 사용했다. 하지만 6번째 줄에서 inner
함수를 실행한 결과를 리턴하고 있으므로 결과적으로 outer
함수의 실행 컨텍스트가 종료된 시점에서는 a
변수를 참조하는 대상이 없어진다. 이 전의 예시와 마찬가지로 a
, inner
변수의 값들은 가비지 컬렉터의 수집 대상이 될 것이다. 역시 일반적인 함수 및 내부함수에서의 동작과 차이가 없다.
마무리
클로저란 어떤 함수에서 선언한 변수를 참조하는 내부함수를 외부로 전달할 경우 함수의 실행 컨텍스트가 종료된 후에도 해당 변수가 사라지지 않는 현상이다.
- 내부함수를 외부로 전달하는 방법에는 함수를
return
하는 경우 뿐 아니라 콜백으로 전달하는 경우도 포함된다. - 클로저는 그 본질이 메모리를 계속 차지하는 개념이므로 더는 사용하지 않게 된 클로저에 대해서 메모리를 차지하지 않도록 관리해줄 필요가 있다.