클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억해야 하므로 메모리 차원에서 손해를 볼 수 있다. 하지만 클로저는 자바스크립트의 강력한 기능으로 이를 적극적으로 사용해야 한다. 클로저가 유용하게 사용되는 상황에 대해 살펴보자.
전역 변수를 사용하지 않고도 현재 상태(혹은 값)를 기억하고 변경된 최신 상태를 유지할 수 있다. 어떤 상태를 관리할 때, 클로저가 없다면 (불가피하게)전역 변수를 사용하거나 아예 로컬스토리지를 사용할 수도 있다.
박스의 색을 토글하는 함수를 전역변수를 사용하지 않고 클로저와 즉시실행함수(IIFE)를 활용하여 구현하였다. 아래 코드에서 즉시실행함수가 반환한 (이름없는) 함수는 렉시컬 환경에 속한 변수를 기억하는 클로저가 된다.
<!DOCTYPE html>
<html>
<body>
<div class="box" style="width: 100px; height: 100px; background: green;"></div>
<script>
// Box Color Toggler
const box = document.querySelector('.box');
const toggleColor = (function () {
let isGreen = true;
// 클로저 반환
return function () {
box.style.background = isGreen ? 'red' : 'green';
// 상태 변경
isGreen = !isGreen;
};
})();
// 박스 클릭 이벤트
box.addEventListener('click', toggleColor);
</script>
</body>
</html>
예시 코드 처럼 클로저를 사용할 경우 전역 변수를 사용하지 않게 된다.
단순히 true , false 토글 이외에도 클로저가 포함된 함수의 변수를 private한 변수 인 것처럼 쓸 수 있을 것이다. 이렇게 불필요한 전역 변수를 사용하지 않음으로서 얻는 이득은 상당하다.
의도되지 않은 변경을 개발자가 걱정할 필요가 없기 때문에 코드가 안정적이게 된다. 필요한 곳에서만 해당 변수에 접근할 수 있게 하고 그 외부에서는 접근하지 못하게 막는 코드는 예상치 못한 부작용을 막을 수 있는 좋은 코드라고 할 수 있다
다른 몇몇 언어들은 프라이빗으로 선언할 수 있는 기능을 제공하여 같은 클래스 내부의 다른 메소드에서만 그 메소드들을 호출할 수 있게 만들어준다.
프라이빗 메소드는 name space를 관리할 수 있게 하고 코드에 대한 접근을 제한적으로 하여 타이트한 스코프를 설계할 수 있게 해준다는 이점이 있다.
자바스크립트에 클래스와 매소드는 있지만, 자체적인 프라이빗 메소드는 따로 없다. 그러나 클로저를 이용하여 마치 프라이빗 메소드인 것처럼 구현할 수 있다.
위에 있었던 toggle 예시와 같은 방식으로는 하나의 함수밖에 클로저로 리턴할 수 없어 구현에 제한이 많다.
하지만 아래 예시 코드와 같이 구현할 경우 다양한 함수를 리턴하여 많은 것을 구현 할 수 있다.
코드는 프라이빗 함수와 변수에 접근하는 퍼블릭 함수를 클로저를 이용해 구현한 코드이며, 이렇게 클로저를 사용하는 것을 모듈 패턴module pattern이라고 한다.
let counter = (function() {
let privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
})();
console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1
counter 라는 객체에 private method increment , decrement , value 를 사용하는 것처럼 구현되었다. counter 내부의 privateCounter 라는 변수에는 개발자가 정해준 (가짜)프라이빗 메소드로만 접근할 수 있는 것이다.