클로저

이재윤·2021년 9월 6일
0

JavaScript

목록 보기
7/10
post-thumbnail

💻 렉시컬 스코프

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

  • [[Environment]] 슬롯
    함수의 정의가 평가되는 단계에서, 함수 객체가 생성될 때, 자신이 정의된 환경에 의해 상위 스코프의 참조를 [[Environment]]에 저장합니다.
const x = 1;
function foo() {
  const x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo();
bar();

foo함수, bar함수 모두 전역에서 선언되었고 전역 코드가 평가되는 시점에 함수 객체를 생성합니다. 이때, [[Environment]]가 저장이 되며 상위 스코프가 전역으로 결정됩니다.

💻 클로저란?

외부 함수보다 중첩 함수가 오래 유지되는 경우, 중첩함수는 이미 생명주기가 끝난(실행 컨텍스트 스택에서 제거된) 외부 함수의 변수를 참조할 수 있습니다. 이러한 중첩 함수를 클로저라고 합니다.

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

const innerFunc = outer();
innerFunc();

위 그림은 전역 평가 과정을 나타낸 그림입니다. outer함수가 평가되어 객체가 생성될 때 현재 실행 중인 실행 컨텍스트의 렉시컬 환경을 [[Environment]]에 상위 스코프로 저장했습니다.

outer함수를 호출한 그림입니다. outer함수를 호출하면 함수의 렉시컬 환경이 생성되며, 외부 렉시컬 함수에 대한 참조가 outer함수 객체의 내부슬롯에 저장된 상위 스코프를 참조하게 됩니다.
❗ inner의 경우 함수 표현식으로 선언하였기 때문에 런타임(실행)과정에서 평가가 일어납니다.

outer함수가 종료된 후의 그림입니다. outer함수가 종료가 되면 실행 컨텍스트는 실행 컨텍스트 스택에서 제거가 됩니다. 하지만 outer함수의 렉시컬 환경까지 제거되진 않습니다. innerFuncouter함수의 렉시컬 환경을 참조하고 있어서 가비지 컬렉션이 메모리에서 제거하지 않기 때문입니다.

outer함수가 반환한 innerFunc을 호출한 그림입니다. inner함수의 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 푸쉬됩니다.

💻 애매한 클로저?

지금까지 클로저를 살펴보았습니다. 이 부분에서는 클로저로 보기 어려운 함수를 살펴볼것 입니다.

function foo() {
  const x = 1;
  const y = 2;
  function bar() {
    const z = 3;
    console.log(z);
  }
  return bar;
}
const bar = foo();
bar();


위의 코드를 끝까지 실행 시켰을때의 모습입니다. 중첩함수인 bar함수가 foo보다 컨텍스트 스텍에 오래 남아있지만, bar함수가 상위 스코프의 어떠한 식별자도 참조하지 않으므로 클로저라 보기 어렵습니다. 이 경우 브라우저는 최적화를 통해 상위스코프를 기억하지 않게 만듭니다.

function foo() {
  const x = 1;
  function bar() {
    console.log(x);
  }
  bar();
}
foo()

코드를 끝까지 실행시킨 모습입니다. bar함수가 상위 스코프의 변수를 참조하고 있지만, bar함수의 수명이 foo함수 보다 짧기 때문에 클로저라 보기 어렵습니다.

💻 클로저의 활용

클로저는 보통 상태를 안전하게 변경하고 유지하기 위해 사용이 됩니다. 즉 상태를 안전하게 보호하고 특정 함수에게만 상태 변경을 허용하기 위해 많이 사용됩니다.

0개의 댓글