코어 자바스크립트 (5. 클로저)

문린이·2022년 11월 15일
0

5-1 클로저의 의미 및 원리 이해

클로저는 어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상이다.

어떤 함수 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

inner 함수의 실행 시점에는 outer 함수는 이미 실행이 종료된 상태인데 outer 함수의 LexicalEnvironment에 어떻게 접근한 걸까?

-> 가비지 컬렉터의 동작 방식 때문이다.

  1. outer 함수는 실행 종료 inner 함수를 반환한다.
  2. 외부 함수인 outer의 실행이 종료되더라도 내부 함수인 inner 함수는 언젠가 outer2를 실행함으로써 호출될 가능성이 있다.
  3. 언젠가 inner 함수의 실행 컨텍스트가 활성화되면 outerEnvironmentReference가 outer 함수의 LexicalEnvironment를 필요로 할 것이므로 수집 대상에서 제외
  4. 그 덕에 inner 함수가 이 변수에 접근이 가능한 것

5-2 클로저와 메모리 관리

메모리 소모에 대한 관리법

클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수를 메모리를 소모하도록 함으로써 발생한다.
그렇다면 그 필요성이 사라진 시점에는 더는 메모리를 소모하지 않게 해주면 된다. (참조 카운트를 0으로)

예시

var outer = function () {
  var a = 1;
  var inner = function () {
    return ++a;
  };
  return inner;
};
var outer2 = outer();
console.log(outer2()); 
console.log(outer2());
outer = null; // outer 식별자의 inner 함수 참조를 끊음

5-3 클로저 활용 사례

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

var fruits = ["apple", "banana", "peach"];
var $ul = document.createElement("ul");

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

클릭 이벤트가 발생하면 afb 함수의 실행 컨텍스트가 열리면서 afb의 인자로 넘어온 fruit를 outerEnvironmentReference에 의해 참조할 수 있다. 즉 afb의 실행 결과로 반환된 함수에는 클로저가 존재

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

정보 은닉은 어떤 모듈의 내부 로직에 대해 외부로의 노출을 최소화해서 모듈간의 결합도를 낮추고 유연성을 높이고자 하는 개념.
접근 권한에는 public, private, protected가 있다.

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

위 예시에서 return한 변수는 공개(public), 그렇지 않은 변수는 비공개(private)

부분 적용 함수

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

var add = function () {
  var result = 0;
  for (var i = 0; i < arguments.length; i++) {
    result += arguments[i];
  }
  return result;
};
var addPartial = add.bind(null, 1, 2, 3, 4, 5);
console.log(addPartial(6, 7, 8, 9, 10)); // 55

addPartial 함수는 인자 5개를 미리 적용하고, 추후 추가적으로 인자들을 전달하면 모든 인자를 모아 원래의 함수가 실행되는 부분 적용 함수이다.

커링 함수

커링 함수란 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출될 수 있게 체인 형태로 구성한 것을 말한다. (커링은 한 번에 하나의 인자만 전달하는 것을 원칙으로 한다.)

const logger = (store) => (next) => (action) => {
  console.log("dispatching", action);
  console.log("next state", store.getState());
  return next(action);
};

위 예제는 store, next, action 순서로 인자를 받는다. 이 중 store, next는 프로젝트 내에서 한 번 생성된 이후로는 바뀌지 않는 속성이지만 action의 경우는 매번 달라진다.

즉, store와 next의 값이 결정되면 logger에 store와 next를 미리 넘겨서 반환된 함수를 저장시켜놓고, 이후에는 action만 받아서 처리할 수 있게 한다.

profile
Software Developer

0개의 댓글