내 생각으로는
클로저를 이해하기 전 스코프와 실행컨텍스트 개념이 선행 된다면 클로저를 이해하기 편할 것 같다.
정의 : 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다. (출처: mdn)
이렇게만 보니 처음에는 이게 무슨 말인가 했다.
하지만 계속 찾아보고 알아보니 핵심이 있었다.
핵심 : 함수가 선언된 렉시컬 환경!!
클로저를 사용하는 목적은 무엇일까?
상태를 안전하게 변경하고 유지하기 위해 사용한다. 즉, 상태를 안전하게 은닉하고 특정 함수에만 상태 변경을 허용하기 위해 사용한다.
이러한 목적으로 클로저를 사용하면 의도치 않은 상태 변경을 막을 수 있다.
그러므로 우리는 클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있다.
내가 이해한 바로 설명을 적으려고 한다.
클로저는 함수를 반환하는 함수이다!
또한 외부함수와 내부함수로 이루어졌고 내부함수는 외부함수의 변수에 접근이 가능하다!
그렇다면 중첩된 모든 함수는 클로저인 것인가?
아니다!!
const x = 1;
function outer(){ // 1번
const x = 10;
const inner = function () { console.log(x) }; // 2번
return inner;
}
// outer 함수를 호출하면 중첩함수 inner를 반환한다.
// 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 pop 된다.
const innerFunc = outer(); // 3번
innerFunc(); // 4번 , 10
위 코드를 보면 outer 함수를 호출(3번)하면 outer 함수는 중첩함수 inner를 반환하고 생명주기가 끝난다. 즉, outer함수의 실행이 종료되면 outer함수의 실행 컨텍스트는 실행컨텍스트 스택에서 제거된다.
그런데 4번을 보게 되면 10이라는 값을 반환한다. 이는 outer함수의 지역 변수 x인 값이다.
이렇게 외부 함수보다 중첩함수가 더 오래 유지되는 경우 중첩함수는 생명주기가 끝난 외부함수의 변수를 참조할 수 있다 (클로저의 조건)
조금 더 쉽게 말하자면 outer 함수의 실행 컨텍스트가 실행컨텍스트 스택에서 제거되지만 outer 함수의 렉시컬 환경까지 소멸하지 않기 때문에 위와 같이 클로저로서 동작할 수 있는 것이다.
(outer 함수의 렉시컬 환경이 inner 함수의 내부 슬롯에 의해 참조되고 있고 inner 함수는 전역변수 innerFunc에 의해 참조되고 있으므로 가비지컬렉션의 대상이 되지 않기 때문.)
(-> 참고로 가비지 컬렉터는 참조되고 있는 메모리 공간을 함부로 해제하지 않는다.)
그림으로 이해해보기
1. 전역 함수 객체의 상위 스코프 결정 과정
2. 중첩 함수의 상위 스코프 결정 과정
3. outer 함수의 실행 컨텍스트 제거, 하지만 outer 함수의 렉시컬 환경은 유지
4. 외부 함수가 소멸해도 반환된 중첩 함수는 외부 함수의 변수를 참조할 수 있다.
출처 및 참고 - 모던 자바스크립트 Deep Dive (이응모)