클로저 Closure

클로저 Closure

클로저

어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상을 말합니다.

다른 표현들

  • 함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수

  • 이미 생명 주기가 끝난 외부 함수의 변수를 참조하는 함수

  • 자신이 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수들만을 기억하여 유지시키는 함수

var outer = function () {
    var a = 1;
    var inner = function () {
        return ++a;
    };
    return inner;
};

var outer2 = outer();
console.log(outer2()); // 2
console.log(outer2()); // 3

클로저가 발생하는 이유

가비지 컬렉터는 어떤 값을 참조하는 변수가 하나라도 있다면 그 값은 수집 대상에 포함시키지 않습니다. 외부함수인 outer의 실행이 종료되더라도 내부함수인 inner 함수는 언젠가 outer2를 실행함으로써 호출될 가능성이 있습니다. 언젠가 inner 함수의 실행 컨텍스트가 활성화되면 outerEnvironmentReference가 outer 함수의 LexicalEnvironment를 필요로 할 것이므로 수집 대상에서 제외됩니다. 그 덕에 inner 함수가 이 변수에 접근할 수 있는 것입니다.

클로저의 메모리 관리 방법

클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수를 메모리를 소모하도록 함으로써 발생합니다. 따라서 그 필요성이 사라진 시점에는 더는 메모리를 소모하지 않게 만들어야 합니다. 참조 카운트를 0으로 만들면 언젠가 GC가 수거해갈 것이고 이때 소모됐던 메모리가 회수될 것입니다. 참조 카운트를 0으로 만드려면 식별자에 참조형이 아닌 기본형 데이터(보통 null이나 undefined)를 할당하면 됩니다.

var outer = function () {
    var a = 1;
    var inner = function () {
        return ++a;
    };
    return inner;
};

var outer2 = outer();
console.log(outer2()); // 2
console.log(outer2()); // 3
outer = null; // outer 식별자의 inner 함수 참조를 끊음

클로저의 활용 사례?

  1. 콜백 함수 내부에서 외부 데이터를 사용하고자 할 때

  2. 접근 권한 제어(정보 은닉)

  3. 부분 적용 함수

  4. 커링 함수

자바스크립트에서 접근 권한 제어(정보 은닉)를 하려면?

자바스크립트는 기본적으로 변수 자체에 이러한 접근 권한을 직접 부여하도록 설계되어 있지 않지만 클로저를 이용하면 함수 차원에서 public한 값과 private한 값을 구분하는 것이 가능합니다. 외부에서는 오직 outer 함수가 return한 정보에만 접근할 수 있습니다. return 값이 외부에 정보를 제공하는 유일한 수단인 것입니다. 외부에 제공하자 하는 정보들을 모아서 return하고, 내부에서만 사용할 정보들은 return하지 않는 것으로 접근 권한 제어가 가능합니다.

클로저를 활용해 접근권한을 제어하는 방법

먼저 함수에서 지역변수 및 내부함수 등을 생성합니다. 그리고 외부에 접근권한을 주고자 하는 대상들로 구성된 참조형 데이터를 return합니다 return한 변수들은 공개 멤버(public)가 되고, 그렇지 않은 변수들은 비공개 멤버가 됩니다

부분 적용 함수

n개의 인자를 받는 함수에 미리 m개의 인자만 넘겨 기억시켰다가, 나중에 (n-m)개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있게끔 하는 함수입니다.

부분 적용 함수를 활용하여 디바운스 코드를 작성하면?

var debounce = function (eventName, func, wait) {
    var timeoutId = null;
    return function (event) {
        var self = this;
        console.log(eventName, 'event 발생');
        clearTimeout(timeoutId);
        timeoutId = setTimeout(func.bind(self, event), wait);
    };
};

var moveHandler = function (e) {
    console.log('move event 처리');
};
var wheelHandler = function (e) {
    console.log('wheel event 처리');
};

document.body.addEventListener('mousemove', debounce('move', moveHandler, 500));
document.body.addEventListener('mousemove', debounce('mousewheel', wheelHandler, 700));

커링 함수

여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출될 수 있게 체인 형태로 구성한 것을 말합니다. 커링은 한 번에 하나의 인자만 전달하는 것을 원칙으로 합니다. 또한 중간 과정상의 함수를 실행한 결과는 그 다음 인자를 받기 위해 대기만 할 뿐이므로, 마지막 인자가 전달되기 전까지는 원본 함수가 실행되지 않습니다. (부분 적용 함수는 여러 개의 인자를 전달할 수 있고, 실행 결과를 재실행할 때 원본 함수가 무조건 실행됩니다.)

var curry3 = function (func) {
    return function (a) {
        return function (b) {
            return func(a, b);
        };
    };
};

var getMaxWith10 = curry3(Math.max)(10);
console.log(getMaxWith10(8)); // 10
console.log(getMaxWith10(25)); // 25

var getMinWith10 = curry3(Math.min)(10);
console.log(getMaxWith10(8)); // 8
console.log(getMaxWith10(25)); // 10