logo

클로저

Chapter 21

60 조회

0 추천

657 단어

4분 예상

2024. 08. 19. 게시

2025. 02. 27. 수정

luasenvy 작성

CC BY-NC-SA 4.0

클로저

function makeClosure() { // 외부함수
  const name = "luasenvy"; // 외부함수 멤버

  return function someFn() { // 내부함수
    return name; // 외부함수 멤버 참조
  }
}

const fn = makeClosure();
fn(); // "luasenvy"

클로저는 선언적인어휘적인 실행환경을 일컫는다. 말이 좀 어려운데 위 예시를 보면 makeClosure() 함수는 someFn() 함수를 반환한다. someFn 함수는 makeClosure 함수의 내부에서 선언된 함수로 외부함수에서 선언된 name을 접근할 수 있고 이 값을 반환하고 있다. 이 연결을 잘 생각해보면 makeClosure -> someFn -> makeClosure.name 이런 식으로 참조하고 있다.

  • makeClosure() 실행
  • someFn 함수 반환 -> fn 변수에 할당
  • fn() 실행

name이라고 하는 외부함수 멤버는 외부함수가 실행중일 때만 유효하다. makeClosure 밖에서 존재하지 않는 변수임은 너무나도 당연해보인다. 또한, someFn()을 실행할 때에도 그 어디에도 name 변수를 초기화 하는 코드는 없다. 직관적으로 보았을 때 someFn()을 실행하고 name을 참조하는 순간에 Uncaught ReferenceError: name is not defined 오류가 발생할 것으로 예상할 수 있다.

그러나 자바스크립트는 someFn이 반환될 때 선언된어휘적 위치에 맞는 환경컨텍스트인 클로저를 구성하여 이 참조들이 올바르게 작동할 수 있도록 유지시킨다. 이러한 작동은 여러가지를 가능하게 해주지만 유지보수가 복잡해지고 예상하지 못했던 동작을 만날 수도 있다.

응용1. 함수 팩토리

const makeFn = (width) => {
  return (el) => {
    el.style.width = width
  };
}

const changeWidth100 = makeFn(100);
const changeWidth200 = makeFn(200);

div1.addEventListener(e => changeWidth100(e.target));
div2.addEventListener(e => changeWidth200(e.target));

이 클로저를 활용하면 함수 팩토리처럼 사용할 수 있다.

응용2. 비공개 멤버 구현

const human = (() => {
  let age = 0;
  const moreThanFive = () => age > 5;
  
  return {
    isOverFive() {
      return moreThanFive();
    },
    setAge(_age) {
      age = _age;
    },
    getAge() {
      return age;
    },
  };
})();

console.info(human.getAge());

과거 자바스크립트에서는 private 멤버를 만들 수 있는 문법이 제공되지 않았기 때문에 위처럼 클로저를 응용하여 비공개 멤버를 흉내낼 수 있도록 하였다. moreThanFive(), age 변수는 클로저가 생성되면서 참조를 잃지 않고 유지되지만 외부에서 접근하려면 isOverFive(), getAge(), setAge()와 같은 명시적으로 공개한 메서드를 사용해야만 한다.