코어 자바스크립트05클로저

dabin *.◟(ˊᗨˋ)◞.*·2021년 8월 7일
0

Javascript

목록 보기
23/25
post-thumbnail

클로저란?

어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상이다. 즉, 외부 함수의 LexicalEnvironment가 가비지 컬렉팅되지 않는 현상이다!

var A = function () {
  var a = 1;
  var B = function () {
    return ++a;
  };
  return B;
};
var A2 = A();
console.log(A2()); //2
console.log(A2()); //3

만약 함수 B가 아닌 결과인 B()를 return했다면 B 변수의 값들은 언젠가 가비지 컬렉터에 의해 소멸했을 것이다. 꼭 return이 아니더라도, 지역변수를 참조하는 내부함수를 외부에 전달하면 클로저가 된다.

클로저의 메모리 관리

  1. 메모리 소모는 클로저의 본질적 특성이며, 이러한 특성을 이해하고 활용해야 한다.
  2. 클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수를 메모리를 소모하도록 함으로써 발생하게 되는데, 필요성이 사라진 시점에 더이상 메모리를 소모하지 않게 만들면 된다.
  3. 참조 카운트를 0으로 만들기 위해서는 식별자에 참조형이 아닌 기본형 데이터(보통 null/undefined)를 할당하면 된다.
var A = function () {
  var a = 1;
  var B = function () {
    return ++a;
  };
  return B;
};

A = null; // A 식별자의 B 함수 참조를 끊음.

클로저 활용 사례

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

var fruits = ['apple', 'peach', 'orange']
var $ul = document.createElement('ul')

fruits.forEach(function (fruit) { //a
  var $li = document.createElement('li');
  $li.innerText = fruit;
  $li.addEventListener('click', function () {//b
    alert('your choice is ' + fruit);
  });
  $ul.appendChild($li);
});
document.body.appendChild($ul);
  • a : 클로저가 없다.
  • b : fruit이라는 외부변수를 참조하는 클로저가 있다.

b가 참조할 예정인 변수 fruit에 대해서는 a가 종료된 이후에도 가비지 컬렉터 대상에서 제외되기 때문에 계속 참조가 가능하다.

위의 예제에서는 콜백 함수를 내부함수로 선언해서 외부변수를 직접 참조하는 방법으로 클로저를 사용했다. 이번에는, 콜백 함수를 고차함수로 바꿔 클로저를 적극적으로 활용하는 예제를 확인해보자. alertFruitBuilder의 실행결과로 반환된 함수에는 클로저가 존재한다.

var fruits = ['apple', 'peach', 'orange']
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);
});

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

정보 은닉은 어떤 모듈 내부 로직에 의해 외부로의 노출을 최소화해 모듈간 결합도를 낮추고 유연성을 높이고자 하는 프로그래밍 언어의 중요한 개념 중 하나다. 흔히 접근 권한에는 public, private, protectd가 있다. 자바스크립트는 접근 권한을 직접 부여하도록 설계돼 있지 않지만, 클로저를 이용해 함수 차원에서 public/private 값 구분이 가능하다.

return을 활용해 일부의 변수에 대한 접근 권한을 부여한다.
1. 함수에서 지역변수 및 내부함수 등을 생성
2. 외부에 접근권한을 주고자 하는 대상들로 구성된 참조형 데이터를 return

var A = function () {
  var a = 1;
  var B = function () {
    return ++a;
  };
  return B; //내부함수 반환
};
var A2 = A();
console.log(A2());
console.log(A2());

return B;를 하면 A함수의 지역변수인 a의 값을 외부에서도 읽을 수 있게 된다.

부분 적용 함수

부분 적용 함수란 n개의 인자를 받는 함수에 미리 m개의 인자만 넘겨 기억시켰다가, 나중에 (n-m)개의 인자를 넘기면 원래 함수의 실행 결과를 얻을 수 있게 하는 함수이다. this를 바인딩해야 하는 점을 제외하면 bind의 메서드의 실행결과가 바로 부분 적용 함수이다.

//bind 활용 부분 적용 함수
var add = function () {
  var result = 0;
  for (var i = 0; i < arguments.lentgh; i++) {
    result += arguments[i];
  }
  return result;
};
var addPartial = add.bind(null, 1, 2, 3, 4, 5);
console.log(addPartial(6, 7, 8, 9, 10));

디바운스
실무에서 부분 함수를 사용하기에 적합한 예로는 '디바운스'가 있다. 짧은 시간동안 동일한 이벤트가 많이 발생할 경우 이를 전부 처리하지 않고 처음 또는 마지막에 발생한 이벤트에 대해 한 번만 처리하는 것으로, 성능 최적화에 큰 도움을 준다.
[더 알아보기]
https://css-tricks.com/debouncing-throttling-explained-examples/
https://kellis.tistory.com/142

커링 함수

커링 함수란 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠 순차적으로 호출될 수 있게 체인 형태로 구성한 것이다. 마지막 인자가 전달되기 전까지는 원본 함수가 실행되지 않는다.

var abc = function (func) {
  return function (a) {
    return function (b) {
      return function (c) {
        return func(a, b, c);
      };
    };
  };
};
var getMax = abc(Math.max);
console.log(getMax(1)(2)(3));//3

//화살표 함수로
var abc = func => a => b => c => func(a, b, c);

각 단계에서 받은 인자들을 마지막 단계에서 참조하므로, 마지막 호출로 실행 컨텍스트가 종료된 후에야 한꺼번에 가비지 컬렉터의 수거 대상이 된다. 이는 결국 마지막 인자가 넘어갈 때까지 함수 실행을 미루는 셈인데, 이를 함수평 프로그래밍에서는 지연실행이라고 한다.

이는 자주 쓰이는 함수의 매개변수가 항상 비슷하고 일부만 바뀌는 경우 유용하다. 공통적인 요소는 먼저 기억시켜두고 특정한 값만으로 서버 요청을 수행하는 함수를 만들면 효율적이다.

profile
모르는것투성이

0개의 댓글