코어 자바스크립트를 꼼꼼하게 정독하고
복습하며 정리한 내용을 토대로 F-lab 멘토링을 받고있다.
이 글은 멘토링으로 알게된 것들을 다시한번 정리해보는 글이다.


클로저

1
2
3
4
5
6
7
8
const outer = function () {
  let a = 1;
  const ineer = function () {
    console.log(++a);
    // inner 함수 내 a변수가 없기 때문에 바깥 스코프의 a를 참조
  };
  inner();
};

몇몇 상황에서 지역변수는 해당 함수가 처리되는 동안에만 존재한다.
outer 함수 실행이 끝나면 참조가 필요 없어진 값(a, inner)들은 가비지컬렉터에 의해 제거된다.



1
2
3
4
5
6
7
8
9
const outer = function () {
  let a = 1;
  const inner = function () {
    return ++a;
  };
  return inner;
};
const innerFunc = outer(); // outer의 실행으로 반환되는 inner 함수가 담긴다
console.log(innnerFunc()); // a : 2

이번에는 outer가 inner의 실행 결과가 아닌, inner 함수 자체를 반환했다.
반환된 inner를 담은 innerFunc변수를 실행하면 inner함수가 실행되면서 a의 값을 증가시켜 출력한다.

클로저는 내부 함수에서 외부 스코프에 있는 변수를 참조하면서 발생한다.
내부 함수가 외부 스코프의 변수를 계속 참조할 필요가 있는 상황에서 외부함수가 종료된다해도 계속적인 참조가 가능하다.
자바스크립트는 함수가 정의되는 시점에 상위 스코프에 대한 참조도 함께 저장하기때문에
참조값이 계속 남아있어 가비지컬렉팅 되지 않는다.

위 코드처럼 함수 자체를 반환받아 외부에서 실행하는 경우가 아니더라도
setInterval setTimeout eventListener 등의 사용으로도 클로저가 발생한다.



클로저와 메모리

책에서는 가비지컬렉팅되지 않고 남아 메모리 누수를 일으킬 수 있는 상황에 대해 설명하고
클로저의 필요가 사라진 경우 참조번수를 null 혹은 undefined로 재할당해 참조카운트를 해지하면 된다고 설명한다.

하지만 우리는 개발자 입장에서 메모리 누수를 걱정하거나 의도적으로 null값을 설정하는 것은 의미가 없다.
어떤 함수와 식별자를 어떻게 활용할 것인가에 중점을 두고
클로저가 발생한 함수가 더이상 필요없다는것을 명시적으로 표현해주면 된다.
내부 함수를 최소한으로 군더더기없이 정의한다면 메모리는 자동으로 관리될 것이다.
코드가 가지는 의미에 더 집중하도록 하자.



클로저의 활용

[ 접근제어를 통한 정보은닉 ]

클로저는 상태가 의도치 않게 변경되지 않도록 안전하게 은닉하기 위해 사용된다.
내부로직의 노출을 줄이면 다른 모듈에서 해당 모듈을 참조 및 관여하는 경우가 적어져 결합도를 낮출 수 있고,
모듈간의 결합도가 낮으면 (=의존도가 낮으면) 서로 영향을 덜받기 때문에 유연성을 높인다고 할 수 있다.

외부에 제공하고자 하는 정보만 return 함으로서 접근권한 제어가 가능해진다.

1
2
3
4
5
6
7
8
const car = {
  fuel: 1000,
  run(distance) {
    const leftFuel = this.fuel - distance;
    return leftFuel;
  },
};
console.log(car.run(10));


이러한 car 객체가 있다면 car.fuel = 10 등의 방법으로 내부 변수를 조작할 수 있어 예외동작이 발생할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
const createCar = function () {
  const fuel = 1000;
  const run = function (distance) {
    const leftFuel = fuel - distance;
    return leftFuel;
  };
  return run;
};

const car = createCar();
console.log(car(10));

클로저를 통해 필요한 값만 return 하여 사용하게 할 수 있다.



[ 커링함수 ]

당장 필요한 정보를 각각 받아놓고 마지막 인자가 들어올때까지 실행을 미루는 방식을 지연실행이라고 한다.
커링함수를 통해 원하는 시점까지 지연시켰다가 실행하는 것이 필요한 상황에 활용할 수 있다.

1
const curry = (func) => (a) => (b) => func(a, b);



[ 부분적용 함수 ]

부분적용 함수는 미리 일부 인자만 넘겨놓았다가, 나중에 나머지 인자를 넘기면 비로소 실행하는 함수이다.
여러개의 인자를 전달 할 수 있고 일부 인자를 받을때와 나머지 인자를 받을때 모두 원본함수가 실행된다.
커링함수는 마지막 인자가 전달되기 전까지 원본함수가 실행되지 않는다.

lodash 라이브러리에서 짧은시간 여러번 발생하는 이벤트에 대해
마지막에 한번만 처리하게하는 debounce 함수가 부분적용 함수의 활용 예제라고 할 수 있다.

Leave a comment