언제가는 한번 정리가 필요했는데... 왜 이렇게 정리하는게 힘들까....

 

Closure란

함수와 렉시컬(Lexical) 환경의 조합. 

함수가 생성될 당시의 외부 변수를 기억하여 생성 이후에도 계속 접근 가능한 함수.

 

 

Global하게 어디서든 호출할 수 있는 add함수를 만들자.

function add() {
  let counter = 0;
  counter += 1;
  return counter;
}

add(); //1
add(); //1
add(); //1

함수 호출할때 마다 counter변수가 초기화 되어 결과가 3이 되지 않는다.

 

Nested Functions를 만들어 함수 밖의 counter변수에 접근할 수 있게 _add함수를 변경하자. 

function _add() {
  let counter = 0;
    
  return function () {
    return ++counter;
  };
}

let add = _add();

add(); // 1
add(); // 2
add(); // 3

실행하면 원하는 데로 작동한다.

이것은 Nested Functions의 밖의 환경(Lexical)이 보존되기 때문에 가능하다.

함수는 자신의 바깥쪽의 변수를 접근할 수 있는 특징이 있기 때문에, 안쪽함수는 counter변수를 접근할 수 있지만 _add함수 바깥쪽에서는 _add함수 안쪽에 선언된 counter변수를 접근할 수 없다.

즉 _add함수안쪽의 counter변수가 private화 되어 _add함수 밖의 global환경에서 임의로 counter변수를 변경할 수 없다.

 

 

위 로직에 추가로 새로운 변수로 _add함수를 할당하여 실행해보자.

let add2 = _add();

// 새로운 Lexical환경이다.
add2(); // 1
add2(); // 2
add2(); // 3

// 기 작동된 Lexical환경에도 영향이 없다.
add(); // 4

counter변수가 각각의 Lexical환경에서 은닉화되었다.

 

 

이걸 더 단순화 시키고 anonymous self-invoking function함수로 만들면 아래와 같다.

let add = (function () {
  let counter = 0;
    
  return function () {
    return ++counter;
  };
})();

add(); // 1
add(); // 2
add(); // 3

위 소스에처럼 function 정의 전체를 ()로 감싸고 이것을 다시()로 호출하는 self-invoke가 되어 선언과 동시에 단 한번 호출이 된다.

앞의 괄호는 함수정의 괄호, 뒤의 괄호는 호출을 위한 괄호. 즉 (함수정의)(호출)가 되어 정의와 동시에 호출이 되는 것이다.

 

 

self-invoking 참고 : https://blog.daonelab.com/post/20/1969/

 

 

아무튼 이걸 또 Arrow functions으로 만들면 아래와 같다. 결과도 같다.

let add = (() => {
    let counter = 0;
    return function() {
        return ++counter;
    };
})();

add(); // 1
add(); // 2
add(); // 3