클로저(Closure)

Seongkyun Yu·2020년 12월 7일
0

TIL - Javascript

목록 보기
19/28
post-custom-banner

기존 블로그에 작성한 내용을 velog로 이전한 글입니다


1. 클로저

  • 외부 함수가 소멸했음에도 내부함수가 외부함수의 지역변수(자유변수)에 접근할 수 있는 메커니즘.

  • 캡슐화를 통해 외부함수의 지역변수(자유변수)의 오염을 막을 수 있다.


2. 클로저와 렉시컬 환경

실행 컨텍스트 스택에서 외부함수가 제거 되더라도 내부함수를 참조하고 있다면 렉시컬 환경은 소멸하지 않는다.

const x = 1;

// ①
function outer() {
  const x = 10;
  const inner = function () {
    console.log(x);
  }; // ②
  return inner;
}

// 함수 outer를 호출하면 중첩 함수 inner를 반환한다.
// 그리고 함수 outer의 실행 컨텍스트는 실행 컨텍스트 스택에서 pop된다.
const innerFunc = outer(); // ③
innerFunc(); // ④ 10

① : outer 함수 객체의 상위 스코프가 할당됨


② : outer 함수 호출로 outer 렉시컬 환경 생성 및 inner 함수 객체의 상위 스코프가 할당됨


③ : outer 함수가 종료됨과 동시에 innerFunc에 의해 outer 렉시컬 환경이 참조됨


④ : innerFunc 호출과 함께 innerFunc 렉시컬 환경이 만들어지고 실행 컨텍스트 스택에 올라감


3. 클로저라 부르는 함수

  • 모든 함수는 이론상 모두 클로저이다.

  • 일반적으로 외부 함수가 종료되어도 내부 함수가 유지되고, 외부 함수의 변수에 접근 가능한 함수를 클로저라고 부른다.


4. 클로저의 활용

  • 캡슐화를 통한 은닉(Information hiding)을 위해 사용한다

    const visitors = {};
    
    const counter = (function () {
      // 카운트 상태를 유지하기 위한 자유 변수
      let man = 0;
      let child = 0;
    
      // 클로저를 메소드로 갖는 객체를 반환한다.
      // 객체 리터럴은 스코프를 만들지 않는다.
      // 따라서 아래 메소드들의 상위 스코프는 즉시 실행 함수의 스코프이다.
      return {
        // child: 0, // 프로퍼티는 public이므로 정보 은닉이 되지 않는다.
        increaseMan() {
          visitors.count = ++man + child; // 상태 변경
        },
        increaseChild() {
          visitors.count = man + ++child; // 상태 변경
        },
        viewVisitors() {
          console.log("성인 수:" + man + "아이 수:" + child);
        },
      };
    })();
    
    counter.increaseMan();
    counter.increaseChild();
    counter.viewVisitors(); // 성인 수: 1 아이 수: 1

    즉시 실행 함수를 사용하여 man과 child가 매번 재정의 되는 것을 막았다.

    또한 man과 child를 변경할 수 있는 것을 내부 함수로 한정하여 변수의 오염을 막았다.

    increaseMan과 increaseChild, viewVisitors함수의 상위 스코프는 즉시 실행 함수이기 때문에 언제 호출되더라도 man과 child에 접근할 수 있다.


5. for과 let을 사용할 때의 클로저

초기화 문에서 let 키워드로 선언한 변수를 사용하면 for 문이 반복될 때마다 for 문 코드의 새로운 렉시컬 환경이 생성된다.

따라서 반복문 내부에 함수 정의가 있을 경우 의미있게 사용할 수 있다.

var arr = [];

for (let i = 0; i < 5; i++) {
  arr[i] = function () {
    return i;
  };
}

for (var j = 0; j < arr.length; j++) {
  console.log(arr[j]()); // 0, 1, 2, 3, 4
}

참고자료: poiemaweb.com

profile
FrontEnd Developer
post-custom-banner

0개의 댓글