[모딥다] 24장.클로저

vanLan·2026년 2월 12일

모딥다

목록 보기
17/25
post-thumbnail

24장. 클로저

  • 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합

📁 렉시컬 스코프

  • JS엔진은 함수를 어디서 호출했는지가 아닌 함수를 어디에 정의했는지에 따라 상위 스코프를 결정.
  • 함수의 상위 스코프를 결정한다는 것은 '실행 컨텍스트' 관점에서 '외부 렉시컬 환경에 대한 참조'에 저장할 값이 '상위 렉시컬 환경에 대한 참조'임.
  • 즉, 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경에 의해 결정됨. 이것이 렉시컬 스코프임.

📁 함수 객체의 내부 슬롯 [[Environment]]

  • 함수는 자신의 '[[Environment]] 내부 슬롯'에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장함.
  • 또한 자신이 호출되었을때 생성될 함수 렉시컬 환경의 '외부 렉시컬 환경에 대한 참조'에 저장될 참조값임.
  • 그렇기 때문에 함수 객체는 상위 스코프(외부 렉시컬 환경)을 자신이 존재하는 한 기억함.

📁 클로저와 렉시컬 환경

const x = 1;

// (1)
function counter() {
  const x = 10;
  const inner = function () { console.log(x); };  // (2)
  
  return inner;
}

const innerFunc = outer();  // (3)
innerFunc();  // 10
  • (3)번에서 outer() 함수가 호출되고 inner 함수를 반환한 후 outer 함수의 실행 컨텍스트는 콜스택에서 제거되지만, 반환된 inner 함수가 innerFunc 변수에 할당 되어 여전히 '내부슬롯 [[Environment]]'에 outer 함수의 렉시컬 환경을 참조하고 있기 때문에 outer 함수의 렉시컬 환경 가비지 컬렉터의 대상이 되지 않기 때문에 소멸하지 않음.
  • 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명주기가 종료한 외부 함수의 변수를 참조할 수 있음. 이를 클로저라고 함.

📁 클로저의 활용

  • 클로저는 상태를 안전하게 변경하고 유지하기 위해 사용함.

  • 즉, 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용됨.

    function createCounter () {
      let num = 0;
      
      return {
        increase: () => { return ++num; },
        decrease: () => { return num > 0 ? --num : 0 }
      }
    }
    
    const counter = createCounter();
    
    counter.increase();  // 1
    counter.decrease();  // 0
    
    // 직접접근 X
    console.log(counter.num);  // undefined

📁 캡슐화와 정보 은닉

  • 캡슐화: 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것.

    • 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 하는데 이를 정보 은닉이라 함.
  • 부적절한 접근으로 부터 객체의 상태 변경을 방지하고, 객체간의 결합도를 낮추는 효과가 있음.

    const Person = (function() {
      let _age = 0;  // private
      
      // 생성자 함수
      function Person(name, age) {
        this.name = name;  // public
        _age = age;
      }
      
      // 프로토타입 메서드
      Person.prototype.sayHi = function () {
        console.log(`Hi! My name is ${this.name}. I am ${_age}.`);
      };
      
      return Person;
    }());
    
    const me = new Person('Lee', 20);
    me.sayHi();  // Hi! My name is Lee. I am 20.
    console.log(me.name);  // Lee
    console.log(me._age);  // undefined
    
    const you = new Person('Kim', 30);
    you.sayHi();  // Hi! My name is Kim. I am 30.
    console.log(you.name);  // Kim
    console.log(you._age);  // undefined
    
    me.sayHi();  // Hi! My name is Lee. I am 30.
  • 위 코드를 보면 여러개의 instance를 생성시 _age 변수의 상태가 유지되지 않는다. 이처럼 정보은닉을 완벽하게 지원하진 않음(단순 정보은닉의 경우 Class의 private을 사용하자)

profile
프론트엔드 개발자를 꿈꾸는 이

0개의 댓글