TIL 56 day 5장 클로저

5069 단어 closercloser

클로저(Closer)

  • 어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상
  • 외부 함수의 LexicalEnvironment가 가비지 컬렉팅되지 않는 현상
  • 어떤 함수 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()를 return 했을 경우 inner 함수는 끝나면서 inner 함수의 실행컨텍스트가 종료되었을 것이다.
  • 위의 예시에서는 outer 함수가 inner 함수를 return 하고 있다.
  • outer2가 실행되면 outer 함수가 반환하는 inner함수가 실행된다.
  • inner 함수 실행 시점에 outer 함수가 실행 종료가 되었는데 어떻게 outer 함수의 LexicalEnvironment에 접근 할 수 있을까?
    => 가비지 컬렉터는 어떤 값을 참조하는 변수가 하나라도 있으면 그 값은 수집대상에 포함시키지 않는다.
    => inner 함수의 실행컨텍스트가 활성화 된다면 outerEnvironmentReference가 outer 함수의 LexicalEnvironment를 필요로 할 것이므로 수집대상에서 제외

클로저와 메모리 관리

  • 메모리 누수 : 개발자의 의도와 달리 어떤 값의 참조 카운트가 0이 되지 않아 GC의 수거 대상이 되지 않는 경우
  • 참조카운트를 0으로 만드는 방법 : 일반적으로 기본형 데이터인 null이나 undefined 할당
var outer = (function () {
  var a = 1;
  var inner = function() {
    return ++a;
  };
  return inner;
})();
console.log(outer());
console.log(outer());
outer = null; // outer 식별자의 inner 함수 참조를 끊음

클로저 활용

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

<방법>
1. 콜백 함수를 내부함수로 선언해서 외부변수를 직접 참조하는 방법
2. bind 메서드로 값을 직접 넘겨 클로저는 발생하지 않으나 제약사항이 있는 방법
3. 콜백함수를 고차함수로 바꿔 클로저를 적극 활용한 방법

// 콜백함수를 고차함수로 바꿔 클로저 활용 방법 예시
var fruits = ['apple', 'banana', 'peach'];
var $ul = document.createElement('ul');

var alertFruitBuilder = function(fruit) {
  return function() {
    alert('your choice is ' + fruit);
  };
};
fruits.forEach(function(fruit) {
  var $li = document.createElement('li');
  $li.innerText = fruit;
  $li.addEventListener('click', alertFruitBuilder(fruit));
  $ul.appendChild($li);
});
document.body.appendChild($ul);

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

  • 정보은닉(information hiding) : 어떤 모듈의 내부 로직에 대해 외부로의 노출을 최소화해서 모듈간의 결합도를 낮추고 유연성을 높이고자 하는 현대 프로그래밍 언어의 중요한 개념
  • JavsScript는 클로저를 사용해 함수 차원의 public한 값과 private한 값 구분 가능
  • return을 사용한 방법 : 클로저 활용으로 외부 스코프에서 함수 내부의 변수들 중 선택적으로 일부의 변수에 대한 접근 권한을 부여
var outer = function() {
  var a = 1;
  var inner = function() {
    return ++a;
  };
  return inner;
};
var outer2 = outer();
console.log(outer2());
console.log(outer2());

=> return 값이 외부에 정볼르 제공하는 유일한 수단

  • 외부에 제공하고자 하는 정보를 모아선 return : 공개 멤버(public member)
  • 내부에서만 사용할 정보는 return하지 않는다. : 비공개 멤버(private member)
    => 접근 권한 제어

3) 부분 적용 함수

  • 부분 적용 함수(partially applied function) : n개의 인자를 받는 함수에 미리 m개의 인자만 넘겨 기억시켰다가 나중에 n-m개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있는 함수
  • this 바인딩이 필요한 점을 제외하면 bind 메서드의 실행 결과가 부분 적용 함수
  • 디바운스(debounce) : 짧은 시간 동안 동인한 이벤트가 많이 발생할 경우 이를 전부 처리하지 않고 처음 또는 마지막에 발생한 이벤트에 대해 한 번만 처리하는 것 => 성능최적화에 도움
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('mousewheel', debounce('move', wheelHandler, 500))

4) 커링 함수

  • 커링함수(currying function) : 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출될 수 있게 체인 형태로 구성한 것
  • 커링은 한 번에 하나의 인자만 전달하는 것을 원칙
  • 마지막 인자가 전달되기 전까지는 원본 함수가 실행 되지 않는다.
var curry5 = function (func) {
  return function(a) {
    return function(b) {
      return function (c) {
        return function (d) {
          return function (e) {
            return func(a, b, c, d, e);
          }
        }
      }
    }
  }
}

var getMax = curry5(Math.max);
console.log(getMax(1)(2)(3)(4)(5)); // 5

var curry6 = func => a => b => c => d => e => func(a,b,c,d,e);
getMax2 = curry6(Math.max);
console.log(getMax2(1)(2)(3)(4)(5)); // 5
  • 화살표 함수를 사용하면 한 줄로 표시 가능
  • 각 단계에서 받은 인자들을 모두 마지막 단계에서 참조
    => GC되지 않고 메모리에 차곡차곡 쌓였다가 마지막 호출로 실행 컨텍스트가 종료된 후에야 비로소 한꺼번에 GC 수거대상이 됨
  • 마지막 인자가 넘어 갈 때까지 함수 실행을 미룬다 => 지연실행(lazy execution)
var getInformation = function(baseUrl) {
  return function(path) {
    return function(id) {
      return fetch(baseUrl + path + '/' + id);
    };
  };
};
// ES6
var getInformation = baseUrl => path => id => fetch(baseUrl + path + '/' + id);
  • 일반적으로 공통적 요소는 먼저 기억시키고 특정값인 id만으로 서버 요청을 수행하는 함수! => 개발 효율성이나 가독성 측면에서 더 좋다!

좋은 웹페이지 즐겨찾기