24 - 클로저 (Closure)

2022. 9. 21.·🎨 프론트엔드 공부/JS & TS

모던 자바스크립트 Deep Dive 정리

클로저란?

▶ 사전적 의미 : 함수와 그 함수가 선언된 렉시컬 환경과의 조합

▶ 외부 함수보다 더 오래 유지되고 상위 스코프의 식별자를 참조하는 (중첩)함수

▷ 함수가 선언된 렉시컬 환경을 이해하자

 

const x = 1;

function outerFunc() {
  const x = 10;

  function innerFunc() {
    console.log(x); // 10
  }

  innerFunc();
}

outerFunc();

 

▶ 중첩함수 innerFunc는 outerFunc의 내부에서 선언되었기 때문에, outerFunc의 x 변수에 접근 가능

▷ 만약, innerFunc 함수가 outerFunc 함수의 내부에서 정의된 중첩함수가 아니면,

innerFunc 함수를 outerFunc 함수 내부에서 호출한다 하더라도 outerFunc  함수의 변수에 접근할 수 없다

▷ 반례

 

const x = 1;

function outerFunc() {
  const x = 10;
  innerFunc();
}

function innerFunc() { // 함수호이스팅 가능
  console.log(x); // 1
}

outerFunc();

 

▶ 이는 자바스크립트가 렉시컬 스코프를 따르기 때문이다

1. 렉시컬 스코프

▶ 렉시컬 스코프 (정적 스코프)

▷ 자바스크립트에서 함수를 어디서 정의했는지에 따라 상위 스코프를 결정하는 것

▷ 렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조값(상위 스코프에 대한 참조) 은

함수가 정의된 환경(위치)에 의해 결정된다. -> 렉시컬 스코프

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

▶ 함수는 자신의 내부 슬롯 [[Environment]]에 자신이 정의된 환경(상위 스코프의 참조) 을 저장한다

▷ 현재 실행중인 실행 컨텍스트의 렉시컬 환경을 저장한다

(함수 정의가 평가되고 있는 실행 컨텍스트이기 때문)

3. 클로저와 렉시컬 환경

▶ 클로저 예시

 

const x = 1;

// ①
function outer() {
  const x = 10;
  const inner = function () { console.log(x); }; // ②
  return inner; // 중첩 함수를 호출
}

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

// 참고로 중첩 함수 inner를 함수 선언문으로 정의해도 결과는 같음

 

▶ ③에서  outer 함수는 호출되고 중첩함수 inner를 반환하고 종료(생명주기 마감 & 실행 컨텍스트에서 제거)

▷ 따라서 실행 컨텍스트에서 제거되면 outer 함수의 지역 변수은 x = 10;도 생명주기 마감

▷ 그런데 ④에서 10이 출력됨

 

▶ outer 함수의 반환값인 중첩함수 inner가 innerFunc 변수에 할당되어 유지됨

▷ 또한 outer 함수의 지역 변수인 x = 10; 을 보존 (inner 함수가 outer 스코프에서 선언되었기 때문)

▷ 중첩 함수가 호출되지 않고 남아있다가 외부함수가 리턴해줘서 살아남을 수 있었음

 

▶ 이처럼 외부 함수보다 중첩 함수가 더 오래 유지되는 경우, 중첩 함수가 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다

▷ 이러한 중첩 함수를 클로저라고 한다

 

▶ 다시 반복하는 클로저의 정의 (MDN)

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

 

▶ outer 함수의 실행이 종료될 때, 실행 컨텍스트가 제거되지만, outer 함수의 렉시컬 환경까지 소멸하지 않음

▷ outer 함수의 렉시컬 환경은 inner 함수의 [[Environment]] 내부 슬롯에 의해 참조되고 있고,

inner 함수는 전역 변수인 innerFunc에 의해 참조되고 있기 때문에 가비지 콜렉션의 대상이 되지 않음

4. 클로저의 활용

▶ 클로저 사용 이유

▷ 상태(state)를 안전하게 변경하고 유지하기 위해 사용

▷ 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고, 특정 함수에게만 변경을 허용

 

▶ counter 프로그램을 클로저로 구현하면 (전역변수 사용 X)

 

const counter = (function () {
  // 카운트 상태 변수
  let num = 0;

  // 클로저인 메서드를 갖는 객체를 반환한다.
  // 객체 리터럴은 스코프를 만들지 않는다.
  // 따라서 아래 메서드들의 상위 스코프는 즉시 실행 함수의 렉시컬 환경이다.
  return {
    // num: 0, // 프로퍼티는 public하므로 은닉되지 않는다.
    increase() {
      return ++num;
    },
    decrease() {
      return num > 0 ? --num : 0;
    }
  };
}());

console.log(counter.increase()); // 1
console.log(counter.increase()); // 2

console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0

 

▶ 즉시실행함수가 클로저를 포함한 객체 리터럴을 반환

▷ 객체 리터럴은 코드 블록이 아니므로 별도의 스코프를 가지지 않음

 

▶ 객체 안의 increase, decrease 함수는 자신이 정의된 위치이자 상위 스코프인 즉시실행함수의 렉시컬 환경을 기억하는 클로저

▷ 이처럼 클로저는 의도치 않은 상태 변경을 방지하고, (은닉) 특정 함수에게만 상태 변경을 허용

5. 캡슐화와 정보 은닉

▶ 캡슐화

▷ 객체의 상태(프로퍼티)와 동작(메서드)을 하나로 묶는 것

 

▶ 정보 은닉

▷ 캡슐화를 특정 프로퍼티나 메서드를 감출 목적으로 사용하는 것

 

▶ 공개할 필요가 없는 프로퍼티나 메서드를 숨겨서 정보 보호

▶ 객체 간의 상호 의존성(결합도)을 낮춰줌

▷ 다른 프로그래밍 언어와 달리 자바스크립트에서는 접근 제한자가 없어 모든 프로퍼티와 메서드가 public임

'🎨 프론트엔드 공부/JS & TS' 카테고리의 다른 글
  • 26 - ES6 함수의 추가 기능
  • 25 - 클래스
  • 23 - 실행 컨텍스트 (Execution Context)
  • 22 - this
지식물원
지식물원
지식이 자라는 식물원!
  • 지식물원
    지식물원
    지식물원
  • 전체
    오늘
    어제
    • 분류 전체보기 (516)
      • 🎨 프론트엔드 공부 (253)
        • JS & TS (92)
        • HTML & CSS (22)
        • React & Next (49)
        • Vue & Nuxt (22)
        • 기타 (68)
      • 🤓 기술 학습 & 공부 기록 (116)
        • Node.js (0)
        • Python (37)
        • 백엔드 (0)
        • 딥러닝 (1)
        • 컴퓨터 일반 (72)
        • 개발 인프라 (6)
      • 👨‍💻 프로젝트 경험 (6)
        • Work (0)
        • Toy (6)
      • ⚙️ 개발 팁 & 노하우 (21)
        • 프론트엔드 (6)
        • 기타 (15)
      • ☕️ 커리어 & 인터뷰 준비 (88)
        • 코딩 테스트 (88)
      • 📰 기술 트렌드 & 생각 정리 (4)
      • 📚 기타 (25)
        • 마케팅 (15)
        • 비개발서적 (10)
  • 블로그 메뉴

    • 태그
  • 링크

  • 공지사항

    • 모바일 접속 시 코드 하이라이팅 깨질 때
  • 인기 글

  • hELLO· Designed By정상우.v4.10.3
지식물원
24 - 클로저 (Closure)
상단으로

티스토리툴바