클로저는 함수형 프로그래밍 언어에서 등장하는 보편적인 특성.
의외로 쉬운 개념이지만 문장만 놓고 보면 이해하기 쉽지 않음 1
클로저를 나타내는 다양한 표현
- 자신을 내포하는 함수의 컨텍스트에 접근할 수 있는 함수 - 더글라스 크록포드
- 함수가 특정 스코프에 접근할 수 있도록 의도적으로 스코프를 정의하는 것 - 에단 브라운
- 함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 잇는 함수 - 존 레식
- 자유변수가 있는 함수와 자유변수를 알 수 있는 환경의 결합 - 에릭 프리먼
- 자신의 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수들만을 기억하며 유지시키는 함수 - 유인동
MDN(Mozila Developer Network)에 클로저
함수와 그 함수가 선언될 당시의 lexical environment의 상호관계에 따른 현상
렉시컬 스코프?
var x =1; // 변수x 선언
function fooFunc(){
var x = 10;
barFunc();
}
function barFunc(){
console.log(++x);
}
fooFunc(); // x = ?
barFunc(); // x = ?
위 코드 실행결과는 함수 barFunc의 상위 스코프에 따라 결정되고 2가지로 생각할 수 있다.
프로그래밍 언어는 이 두가지 방식 중 하나의 방식으로 함수의 상위 스코프를 결정하고
첫번째 방식을 동적 스코프 두번째 방식을 렉시컬 스코프 또는 정적 스코프라고 한다.
실행컨텍스트의 관점에서 스코프 체인이 바인딩한 객체는 곧 렉시컬 스코프가 된다.
상호관계?
function fooFunc(){
var x = 1
var barFunc = function(){
return ++x;
}
return barFunc;
}
// fooFunc를 호출하면 내부 barFunc이 반환되고
// 함수 fooFunc의 실행컨텍스트는 소멸된다.
var barFunc = fooFunc();
console.log(barFunc()); // 2
console.log(barFunc()); // 3
위 코드를 fooFunc은 내부 barFunc을 반환하고 콜(실행컨텍스트)스택에서 제거되고 fooFunc에 선언된 변수 x또한 유효하지 않게되어 접근할 수 있는 방법이 없어진다.
그러나 코드의 동작을 보면 제거된 fooFunc의 지역변수 x가 부활이라도 한 듯 동작하고 있다.
자신을 포함하고 있는 외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부함수가 호출되더라도 외부함수의 지역변수에 접근할 수 있는데 이러한 함수를 클로저(Closure)라고 부른다.
조금더 간단히 말하면 클로저는 자신이 생성될 때의 환경을 기억하는 함수
버튼 상태제어를 위한 클로저 활용
var button = document.createElement("button");
button.textContent="click";
var toggle = (function(){
var isBlack = false;
return function(){
this.style.color = isBlack ? 'black' : 'red';
isBlack = !isBlack;
}
})();
button.addEventListener("click", toggle )
document.body.append(button)
클로저와 메모리 관리
클로저는 어떤 필요에 의해 함수의 지역변수를 의도적으로 사용하기 때문에 관리를 잘하지 않으면 메모리 누수가 발생할 수 있다. 필요성이 없어진 클로저는 참조카운트 제거를 위해 null 혹은 undefined를 할당해 준다.
button.removeEventListener("click", toggle )
toggle = null;
1 : 책에 그렇게 적혀있다는 글입니다.