Javascript 클로저
클로저(Closure)란?
클로저란 자바스크립트 고유의 개념이 아니라 ECMAScript 에는 정의되어 있지 않다.
MDN 에는 아래와 같이 정의하고 있다.
“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
위 정의를 보면 도무지 이해할 수가 없다.
아래의 예시를 보며 확인해보자.
function parentF(){
var x = 10;
var childF = function(){
console.log(x);
}
childF();
}
parentF();
// 결과 : 10
위 전역에 존재하는 parentF() 함수를 호출하고 parentF() 함수는 내부에 있는 childF() 를 호출하고 있다.
childF() 함수는 자신을 포함하고 있는 상위 스코프인 parentF() 함수의 x 에 접근 및 호출하고 있다.
childF() 함수가 x 변수에 접근할 수 있는 이유는 childF() 함수가 parentF() 함수 내부에 선언되어 있기 때문이다.
자바스크립트는 렉시컬 스코프(Lexical Scope) 를 따른다.
렉시컬 스코프(Lexical Scope)
렉시컬 스코프는 함수를 호출한 위치가 중요한게 아닌 선언한 위치에 따라 상위 스코프가 정해진다.
이처럼 렉시컬 스코프에 의해 parentF() 함수 내부에 선언되어 있는 childF() 함수의 상위 스코프는
parentF() 함수가 되므로 변수 x 에 접근할 수 있다.
childF() 가 parentF() 함수 내부가 아닌 parentF() 함수처럼 전역에 선언되어 있다면 상위 스코프는
전역 스코프가 된다.
아래 또 다른 방법의 예시를 보자.
function parentF(){
var x = 10;
var childF = function(){
console.log(x);
};
return childF;
}
var child = parentF();
child();
// 결과 = 10
parentF() 함수를 호출하게 되면 내부함수인 childF() 함수를 반환하는 형식으로 변경했다.
parentF() 를 통해 parentF() 함수 실행 컨텍스트를 콜 스택에 담아 실행하고 childF() 함수를 반환한 후
콜 스택에서 제거된다.
실행 컨텍스트에서 제거되어 변수 x 에 접근할 수 없을줄 알았지만 아니였다.
반환받은 함수를 저장하는 child 변수를 실행해보면 결과 값이 10인걸 확인할 수 있다.
이미 콜 스택에서 코드를 실행하고 실행 컨텍스트에서 제거됐지만 parentF() 함수의 변수 x 에 접근할 수 있었다.
이처럼 반환한 자신을 포함하고 있는 외부 함수 즉, 상위 스코프보다 내부 함수가 더 오래 유지되는 경우
외부 함수 밖에서 내부함수가 호출되더라도 외부함수의 지역 변수에 접근할 수 있는데
이러한 함수를 클로저(Closure) 라고 한다.
다시 MDN 의 정의를 봐보자.
“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
클로저는 반환된 내부함수가 자신이 선언됐던 외부함수 안의 환경(렉시컬 환경, Lexical environment)인 스코프를
기억하여 자신이 선언됐을 때의 스코프(환경) 밖에서 접근할 수 있는 함수를 말한다.
클로저는 자신이 생성되고 선언 됐었던 환경(렉시컬 환경, Lexical environment)을 기억하는 함수이다.