JavaScript: 클로저(Closure)

Beautify.log·2021년 11월 9일
0
post-thumbnail

안녕하세요! 오랜만의 모던 자바스크립트 튜토리얼입니다.

이번 포스팅에서는 클로저(Closure)에 대해서 살펴보려고 합니다.

클로저의 기본 이해

클로저는 "자기 자신이 정의된 환경에서 함수 안에 있는 자유 변수의 식별자 결정을 실행"하는 자바스크립트의 자료 구조 모음입니다.

예를 들어서 다음과 같은 코드가 있다고 가정해보겠습니다.

const p = "P";

function x () {
  const q = "Q";
  function y () {
    const r = "r";
    console.log(p + q + r);
  }
  y();
}
x();

여기에서 함수 x에 정의된 함수 y는 자신의 바깥 영역, 즉 변수 q와 함수 x를 그리고 변수 p를 참조합니다.

즉, 함수 x를 호출할 때 그에 대한 렉시컬 환경 컴포넌트가 생성되고, 이후에 함수 y의 선언문을 평가해서 함수 객체를 만들어줍니다. 함수 객체의 렉시컬 환경 컴포넌트에는 함수 y의 코드와 함수 x의 렉시컬 컴포넌트 참조, 전역 객체 참조가 저장되게 됩니다.

함수 y를 호출하여 실행하면 그 시점에서 함수 y의 렉시컬 환경 컴포넌트를 생성하게 되고 동시에 함수 y의 실행문맥의 외부 렉시컬 환경 참조를 체인처럼 거슬러 올라 변수 pq의 값을 참조하게 됩니다.

클로저의 기본적인 성질

카운터를 만들어주는 함수를 작성해보겠습니다.

function makeCounter() {
  let count = 0;
  function f() {
    return count++;
  }
  return f;
}

let csCounter = makeCounter();
console.log(csCounter());	// 0
console.log(csCounter());	// 1
console.log(csCounter());	// 2
console.log(csCounter());	// 3

csCounter()를 호출할 때마다 count에 1씩 더해주는 함수입니다.

이 함수는 중첩함수 f의 참조를 반환해주고, 중첩 함수 f는 외부 함수 makeCounter()의 지역변수인 counter를 참조하게 됩니다.

여기서 중요한 사실을 하나 더 캐치하자면 함수 makeCount 내부에 선언된 변수 count는 함수라고 하는 블럭(block) 내부에 있기 때문에 외부에서 참조할 수 없습니다.

조금 더 생각해보면 함수 객체의 프로퍼티를 외부에서 읽고 쓰는 것은 가능하지만 클로저 내부 상태는 외부로부터 숨겨져 있기 때문에 변수를 은닉하여 지속성을 보장할 수 있다고 말할 수 있겠습니다.

이처럼 객체 지향 프로그래밍에서 객체의 프로퍼티를 외부에 대해 은폐하는 것을 캡슐화라 하고 클로저는 캡슐화된 객체라고 표현할 수 있겠습니다.

클로저를 이해하기

클로저를 더욱 심도있게 이해해봅시다.

  1. 외부 함수를 호출하면 그 함수의 lexical environment component가 생성되고 그 내부에 중첩함수의 함수 객체를 생성하여 반환합니다. 그리하여 외부 함수의 lexical environment component를 참조하는 중첩 함수가 정의한 클로저가 생성되는데 외부 함수는 클로저를 생성하는 팩토리 함수라고 할 수 있습니다.
  2. 외부 함수가 속한 lexical environment component는 클로저 내부 상태 자체이므로 외부 함수가 호출될 때마다 새로 생성됩니다.
  3. 중첩 함수 객체가 있는 한 외부 함수가 속한 lexical environment component는 지워지지 않고 외부 함수의 함수 객체가 사라져도 지워지지 않습니다.
  4. 클로저 내부 상태(외부 함수의 지역변수, 선언적 환경 레코드)는 외부로부터 은폐되어 있고 중첩 함수 안에서만 읽고 쓸 수 있습니다.

클로저를 예제로 살펴보기

모던 자바스크립트 입문(이소 히로시, 2018)에 좋은 예제들이 있어서 가져와봤습니다.

우선 위에서 다룬 예시를 응용한 것입니다. 여러 내부 상태와 메서드를 가진 클로저를 생성하는 함수를 만들 수 있습니다. 또한 메서드를 여러가 가진 객체를 반환하는 함수를 구현할 수 있습니다.

function Person(name, age) {
  let _name = name;
  let _age = age;
  return {
    getName: function() { return _name; },
    getAge: function() { return _age;},
    setAge: function(x) { _age: x;}
  };
}

const person = Person("Tom", 18);
console.log(person.getName());
console.log(person.getAge());
person.setAge(19);
console.log(person.getAge());

클로저를 활용하여 다양한 매개변수를 받는 함수를 만들 수 있습니다.

function makeMultiplier(p) {
  return function(q) {
    return p * q;
  };
}

let multi2 = makeMultiplier(2);
let multi10 = makeMultiplier(10);

console.log(multi2(3));
console.log(multi10(3));

정리

클로저는 자기 자신이 속해있는, 정의된 함수 안에서 자유 변수 식별자의 결정을 실행하는 자바스크립트의 자료 구조 모음이었습니다!
또한 중요한 것은 이를 활용하여 변수나 다른 객체를 은폐하여 보장성을 확보해 줄 수 있다는 사실도 매우 중요한 포인트였습니다.

클로저를 활용하여 다양한 프로그램을 작성해보세요!

이번 포스팅은 이것으로 마치겠습니다.

profile
tried ? drinkCoffee : keepGoing;

0개의 댓글