24장 클로저

seo0·2023년 4월 2일
0

JavaScript

목록 보기
24/26
post-thumbnail

클로저는 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어에서 사용되는 중요한 특성이다.

"A closure is the comvination of a function and the lexical environment within which that function was declared"
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.




렉시컬 스코프

자바스크립트 엔진은 함수를 어디서 호출했는지가 아닌 함수를 어디에 정의했는지에 따라 상위 스코프를 결정하는데 이를 렉시컬 스코프라 한다.

스코프의 실체는 실행 컨텍스트의 렉시컬 환경이다. 렉시컬 환경은 자신의 "외부 렉시컬 환경에 대한 참조"를 통해 상위 렉시컬 환경과 연결되는 스코프 체인을 갖는다.
즉 함수의 상위 스코프를 결정한다는 것은 렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조값을 결정한다는 것과 같은 의미이다.

렉시컬 스코프를 한마디로 설명하면 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조 값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경에 의해 결정되며 이것을 렉시컬 스코프라고 한다.



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

렉시컬 스코프가 가능하려면 함수는 상위 스코프를 기억해야한다. 이를 위해 함수는 자신의 내부 슬롯 [[Environment]]에 자신이 정의된 환경인 상위 스코프의 참조를 저장한다.

한마디로 함수 정의가 평가되어 함수 객체를 생성할 때 자신이 정의된 환경에 의해 결정된 상위 스코프의 참조를 함수 객체 자신의 내부 슬롯 [[Environment]]에 저장한다.

함수 객체는 내부슬롯 [[Environment]]에 저장한 렉시컬 환경의 참조, 즉 상위 스코프를 자신이 존재하는 한 기억한다.

const age = 1;
function outer() {
  const age = 24;
  inner();
}

/*함수의 상위 스코프는 함수 호출위치와 관계 없으며 
정의 환경에 따라 결정되므로 inner 함수는 자신의 상위 스코프인 
전역 렉시컬 환경을 [[Environment]]에 저장한다.*/
function inner() {
  console.log(age);
};

outer();  //1
inner();  //1




클로저

const age = 1;
function outer() {
  const age = 24;
  const inner = function() {console.log(age);};
  return inner;
}

//outer 함수를 호출하면 중첩함수 inner를 반환함
const innerFunc = outer();
innerFunc();  //24

외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저라고 부른다.

클로저가 아닌 함수

자바스크립트의 모든 함수는 상위 스코프를 기억하므로 이론적으로는 모든 함수가 클로저이지만 일반적으로 모든 함수를 클로저라고 하진 않는다.

  1. 상위 스코프의 어떤 식별자도 참조하지 않는 경우
function foo() {
  const x = 1;
  
  function bar() {
    const age = 24;
    //debugger;
    console.log(age);
  }
  return bar;
}

const bar = foo();
bar();  //24
  
  1. 중첩 함수가 외부 함수보다 일찍 소멸되는 경우
function foo() {
  const x = 1;
  
  function bar() {
    //debugger;
    console.log(x);
  }
  bar();
}

foo();  //1

클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 오래 유지되는 경우에 한정하는 것이 일반적이다.

클로저에 의해 참조되는 상위 스코프의 변수를 자유변수라 부르며 클로저란 함수가 자유변수에 대해 닫혀있다는 의미이다.




클로저의 활용

클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다. 즉, 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용된다.

캡슐화는 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것을 말한다. 캡슐화는 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 하는데 이를 정보 은닉이라고 한다.

const counter = (function () {
  let num =0;
  
  return {
    increase() {
      return ++num;
    },
    decrease() {
      return num>0? --num:0;
    }
  };
}());

console.log(counter.increase());  //1
console.log(counter.increase());  //2
console.log(counter.increase());  //3
console.log(counter.increase());  //4
console.log(counter.increase());  //5

console.log(counter.decrease());  //4
console.log(counter.decrease());  //3
console.log(counter.decrease());  //2

위 코드가 실행되면 즉시 실행 함수가 호출되고 즉시 실행 함수가 반환한 객체 리터럴이 counter 변수에 할당된다. counter 변수에 할당된 객체 리터럴은 자신이 정의된 위치에 의해 결정된 상위 스코프인 즉시 실행 함수의 렉시컬 환경을 기억하는 클로저다.
increase, decrease 메서드의 상위 스코프는 메서드가 평가되는 시점에 실행중인 실행 컨텍스트인 즉시 실행 함수 실행컨텍스트의 렉시컬 환경이 된다. 따라서 increase, decrease 메서드는 언제 어디서 호출되는 상관없이 즉시 실행 함수 스코프의 식별자를 참조할 수 있다.








📔출처
위키북스 - 모던 자바스크립트 Deep Dive
https://wikibook.co.kr/mjs/

0개의 댓글