MDN에서 찾아보면 나오는 클로저에 대한 소개이다.
클로저는 자바스크립트가 제공하는 가장 강력한 추상화이며, 동시에 잠재적으로 매우 혼란스러울 수 있는 개념이다.
MDN에서 찾아보면 추상화의 정의에 대해 이렇게 나와있다.
컴퓨터 프로그래밍에서 추상화란 복잡한 소프트웨어 시스템을 효율적으로 설계하고 구현할 수 있는 방법이다. 추상화는 뒷편 시스템의 기술적 복잡함을 단순한 API 뒤에 숨긴다.
그리고, 위키피디아에는 이렇게 정의되어있다.
컴퓨터 과학에서 추상화는 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것을 말한다.
위키피디아에서는 한국어 번역 버전 대신 영어 원문으로 읽기를 추천한다. 내용의 차이가 어마어마하다..
가장 강력한 추상화라는 의미는, 아마도 클로저를 통해 얻을 수 있는 이점들이 추상화의 정의에 가깝다는 의미인 건가...? 클로저와 추상화가 왜 연결되는 건지 아직 잘 모르겠다.
다시 클로저에 대한 설명으로 돌아오자.
MDN에 나오는 클로저의 정의는
lexical environment
)에 대한 참조의 조합이다.
클로저를 이해하기 위해선 어휘적 환경을 둘러싼 렉시컬 스코프에 대한 이해가 먼저 필요하다.
자바스크립트는 함수를 어디에서 정의했는 지에 따라 상위 스코프가 결정되는 렉시컬 스코프(lexical scople
)가 적용된다. 정적 스코프(static scope
)라고 불리기도 한다.
const a = "1";
function funcA() {
const a = "2";
funcB();
}
function funcB() {
console.log(a);
}
funcA(); // 콘솔 결과 : 1
funcB(); // 콘솔 결과 : 1
위 예제의 funcA
, funcB
함수는 둘 다 전역에서 선언된 함수이다. 즉, 두 함수는 전역이 상위 스코프인 것이다. funcB
함수가 funcA
함수 안에서 호출되었지만, 자바스크립트는 렉시컬 스코프를 따르는 언어이기 때문에 funcB
의 상위 렉시컬 스코프는 전역 스코프가 되고 console.log(a)
는 스코프 체인을 통해 전역 변수 a
의 값을 찾아 콘솔에 출력한다.
자바스크립트에서 모든 함수는 바깥의 렉시컬 환경에 대한 참조
를 유지한다.
쉽게 말해서, 자바스크립트에서 모든 함수는 선언될 때 선언된 위치의 상위 스코프를 기억
한다.
간단한 코드를 예시로 들어보겠다.
function outer(){
const variable = "지역_변수_값";
return function inner(){
console.log(`hi, ${variable}`);
}
}
const sayHi = outer();
sayHi(); // 콘솔 결과 : ???
코드의 작동 순서를 예상해보자.
outer
, 변수sayHi
가 선언된다.sayHi
에 outer
함수의 반환값인 inner
함수가 할당되고 return
으로 함수 호출이 끝나면서 outer
함수와 outer
함수의 지역변수 variable
는 생명 주기를 마감한다.sayHi
가 호출되면서, inner
함수가 호출된다.inner
함수가 작동하면서 콘솔이 찍혀야하지만, variable
은 이미 생명 주기가 마감된 변수이기 때문에 어디에서도 참조할 수 없으므로 refernceError
을 띄운다.4번의 상황을 코드로 풀어서 설명해보자면 아래와 같다.
const sayHi = outer(); // 호출 결과 inner 리턴
// 그러므로, sayHi에 inner를 할당하는 것과 같아진다.
sayHi = function inner(){
console.log(`hi, ${variable}`);
}
sayHi();
그런데, 실제로 작동시켜보면 에러는 나오지 않고 콘솔에는 hi, 지역_변수_값
이 찍히게 된다. 이미 생명이 다한 variable
변수가 다시 부활한 것처럼 동작하고 있다.
이처럼 외부 함수보다 중첩 함수가 더 오래 생존하는 함수를 클로저라 부른다.
다시 한번 MDN의 정의를 떠올려보자.
클로저는
함수
와 그 함수가 선언된렉시컬 환경
과의 조합이다.
즉, inner
함수는
자기자신
과 outer
함수의 호출로 인해 선언된 렉시컬 환경
의 조합으로 클로저
를 형성한다.
쉽게 말하면, inner
함수는 선언되면서 선언된 위치인 outer
함수의 렉시컬 환경을 기억한다. outer
함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거되지만 outer
함수의 렉시컬 환경까지 소멸하는 것은 아니다. outer
함수의 렉시컬 환경은 inner
함수가 참조하고 있고 inner
함수는 다시 sayHi
에 의해 참조되고 있으므로 가비지 컬렉션의 대상이 되지 않기 때문이다.
그리고 살아있는 outer
함수의 렉시컬 환경에는 "지역_변수_값"
이 값으로 할당된 variable
변수가 있다.
참고 출처 :
https://stackoverflow.com/questions/111102/how-do-javascript-closures-work
https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
https://developer.mozilla.org/ko/docs/Web/JavaScript/Language_overview#%ED%81%B4%EB%A1%9C%EC%A0%80_closures
모던 자바스크립트 딥 다이브