CoreJavaScript (05) 클로저

LEE JIYUN·2020년 9월 6일
0

CoreJavaScript

목록 보기
2/2
post-thumbnail

이 포스트는 정재남 님의 저서 코어자바스크립트의 Chapter 5. 클로저에 기반합니다.
스스로 이해하고 기억하기 위한 기록이며, 책은 사서 읽읍시다!

Chapter 05. Closure

🔎 클로저의 의미 및 원리 이해

클로저는 자바스크립트 고유의 개념이 아니라 여러 함수형 프로그래밍 언어에서 나타나는 보편적 특성입니다.

  • 함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수
    ( ⏤존 레식, ⌜자바스크립트 닌자 비급⌟, 인사이트 p116 )
  • 이미 생명주기가 끝난 외부 함수의 변수를 참조하는 함수
    ( ⏤송형주 고현준, ⌜인사이드 자바스크립트⌟, 한빛미디어 p157 )
  • 자신이 생성될 때의 스코프에서 알 수 있었던 변수 중 언젠가 자신이 실행될 때 사용할 변수만을 기억하여 유지시키는 함수
    ( ⏤유인동, ⌜함수형 자바스크립트 프로그래밍⌟, 인사이트 p31 )

MDN의 정의

  • A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).
    클로저는 함수와 함수를 감싸는 참조환경의 (Lexical environment) 조합입니다. 무슨 말인지 모르겠습니다.
  • In other words, a closure gives you access to an outer function’s scope from an inner function.
    즉, 클로저는 내부 함수로부터 외부 함수에 접근할 수 있게 합니다.
  • In JavaScript, closures are created every time a function is created, at function creation time.
    자바스크립트에서 클로저는 함수가 생성될 때마다 만들어집니다.

예제

  • 아래 예제에서 변수 a와 inner 변수의 값은 outer 실행 컨텍스트가 종료되는 시점에 GC에 의해 소멸됩니다.
  • 만약 outer 실행 컨텍스트가 종료된 후에도 inner 함수를 호출하고 싶다면?
  • 내부 함수 inner 를 외부로 전달하는 다양한 경우
    ⌲ 내부 함수를 반환 : return inner;
    ⌲ Window의 method에 전달할 callback 함수 내부에서 지역변수를 참조
    setTimeout(inner, 1000); setInterval(inner, 1000);
    ⌲ DOM method의 handler 함수 내부에서 지역변수를 참조

🔎 클로저와 메모리 관리

클로저는 지역변수를 GC의 수집대상에서 의도적으로 제외시키므로, 필요성이 사라진 시점에는 메모리를 소모하지 않도록 참조 카운트를 0으로 만들어 주는 것이 좋습니다. 참조 카운트를 0으로 만들기 위해서는 식별자에 null이나 undefined를 할당하면 됩니다.
⌲ return에 의한 클로저

var outer = function() {
  var a = 1;
  var inner = function() {
    return ++a;
  };
  return inner;
})();
console.log(outer());
outer = null;

⌲ setInterval에 의한 클로저

(function() {
  var a = 0;
  var intervalid = null;
  var inner = function() {
    if(++a > 10) {
      clearInterval(intervalid);
      inner = null;
    }
    console.log(a);
  };
  intervalid = setInterval(inner, 1000);
})();

⌲ eventListener에 의한 클로저

(function() {
  var count = 0;
  var button = document.createElement('button');
  button.innerText = 'click';var clickHandler = function() {
    console.log(++count, 'times clicked');
    if(count > 10) {
      button.removeEventListener('click', clickHandler);
      clickHandler = null;
    }
  };
  button.addEventListener('click', clickHandler);
  document.body.appendChild(button);
})();

🔎 클로저 활용 사례

콜백 함수에서 외부 데이터 사용하기

var fruits = ['banana', 'apple', 'orange'];
var $ul = document.createElement('ul');
  1. 콜백 함수를 내부 함수로 선언하여 외부 변수를 직접 참조하기
  2. 콜백 함수를 고차 함수(함수를 리턴하는 함수)로 바꾸기 : 외부로 분리

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

자바스크립트에서 변수 자체에 접근 권한을 직접 부여할 수는 없지만, 클로저를 이용하면 선택적으로 public / private 한 값을 구분할 수 있습니다. 함수 외부에서는 내부에 어떤 개입도 할 수 없으나, 함수가 반환한 값에는 접근할 수 있으므로 외부에 제공하고자 하는 정보만 반환하고 나머지 정보들은 내부에서만 관리하는 방식으로 접근 권한을 제어하는 것입니다.
다음은 코어자바스크립트에서 소개하는 간단한 자동차 경주 게임 코드입니다. 이용자가 연료, 연비와 이동 거리를 마음대로 변경할 수 없도록 객체를 함수로 바꾸어주면, 조금 더 안전한 코드가 됩니다.

부분 적용 함수

함수에 미리 적용할 인자들을 전달하고, 이후에 나머지 인자를 넘기면 원래 의도하던 함수의 결과를 얻을 수 있게 하는 함수입니다.
하지만 이렇게 하면 미리 적용할 인자와 나머지 인자의 순서를 바꿀 수 없습니다. 나머지 인자는 무조건 미리 적용할 인자의 뒤로 추가되게 되는데, 인자들을 원하는 위치에 미리 배치해두고 나중에 빈 자리를 채워넣을 수 있다면 좋을 것 같습니다. ES5 환경에서는 다음과 같이 코드를 추가합니다.
하지만 ES6에서는 Symbol.for를 활용할 수 있습니다. Symbol.for method는 전역 Symbol 공간에 값이 선언되어 있는 경우 해당 값을 참조하고, 아니라면 새로 만드는 방식입니다. 여기서는 전역 객체에 미리 프로퍼티를 정의하고 속성을 설정할 필요가 없습니다. 전역 객체의 프로퍼티 정의 코드를 삭제하고 아래와 같이 추가 및 수정합니다.

커링 함수

여러 개의 인자를 받는 함수를 각각 하나의 인자만 받는 함수로 나누어 순서대로 호출될 수 있게 구성하는 것을 커링 함수라고 합니다.
아직 공부 중인 Redux의 middleware가 currying function의 예시로 등장했습니다. 다음은 Redux middleware의 logger 함수입니다.

0개의 댓글