JavaScript - Closure

Thomas·2024년 3월 2일
0

모던자바스크립트

목록 보기
2/3
post-thumbnail

클로저는 자바스크립트의 고유 개념이 아닙니다.
함수가 일급 객체로 여겨지는 함수형 프로그래밍 언어에서도 클로저의 개념이 있습니다.
자바스크립트는 멀티 패러다임 언어로써 객체지향, 함수형 프로그래밍 모두 지원하며 함수는 일급객체로써 활용됩니다.

자바스크립트는 함수를 일급객체로 여깁니다. 그렇기에 함수를 변수에 담거나 매개변수로 넘겨주거나 어떤 함수의 리턴 객체로 사용할 수 있습니다.

클로저란?

MDN 의 정의에 따르면 클로저는 "함수와 함수가 선언된 렉시컬 환경의 조합" 이라고 합니다.
문자 그대로 본다면 난해합니다. 외우기도 어렵습니다. 그렇기 때문에 이해하는 과정이 필요합니다.

함수 정의의 평가

먼저 클로저를 이해하기 위해선 자바스크립트의 실행 컨텍스트에 대해 이해하고 있어야합니다. 자바스크립트는 평가 단계와 실행 단계로 나눠집니다. 평가 단계에서 실행 컨텍스트가 생성이되고 실행 컨텍스트에 렉시컬 환경이 생성이 됩니다. (자세한 내용은 생략합니다.) 이때 해당 렉시컬 환경에 정의된 식별자들이 평가가 되는데, 함수의 정의가 평가 됩니다.

함수 정의의 평가 단계에서 실행중인 실행 컨텍스트의 렉시컬 환경 참조값 을 함수 내부의 [[Envrioment]] 라는 슬롯에 추가합니다. 렉시컬 환경 참조값을 내부 슬롯에 담고 있기 때문에 외부의 식별자를 참조할 수 있게 되고, 이를 "함수와 함수가 선언된 렉시컬 환경의 조합" 이라고 설명하고 있습니다.

예문

아래 예문을 살펴보면 outer 함수는 inner 함수를 리턴하고 있습니다.
아래 라인에서 outer 함수가 실행이 되고 inner 함수를 리턴하며 outer 함수는 생명주기를 다해 스택에서 제거가 됩니다.
console 로 inner 함수를 실행하면 2 라는 값이 리턴이 되는데, inner 함수에서 생명주기를 다한 outer 함수의 식별자를 참조하고 있습니다.
inner 함수가 클로저 함수로써 렉시컬 환경을 내부에서 기억하고 외부 식별자를 사용하고 있는 것 입니다.

function outer() {
  const v1 = 1;
  
  function inner() {
    const v2 = v1 + 1;
    return v2;
  }
  
  return inner;
}

const inner = outer();
console.log(inner()); // 2;

리액트에서의 클로저

리액트의 useState 훅은 클로저 함수입니다. useState 의 내부 메커니즘을 살펴보면 렉시컬 환경을 기억하고 활용해 상태를 변경하고 있습니다.

리액트로 개발을 하다보면 컴포넌트를 map 을 활용해 구성하고는 합니다. 자식 컴포넌트의 핸들러 함수에서 해당 컴포넌트 고유의 id 를 받아야 하는 상황이 있을 수 있습니다.
핸들러 함수의 형태는 이미 정해져 있습니다. 그렇다면 함수에서 어떻게 id 를 받을 수 있을까요?

이때 클로저를 이용해볼 수 있습니다.

const Component = () => {
  const handleClick = (id: number) => () => {
    console.log(id);
  };
  
  return (
    <div>
      {list.map((li) => <li onClick={handleClick(li.id)}>{li.label}</li>)}
    </div>
  );
}

위 함수를 살펴보면 컴포넌트를 map 할 때 handleClick 함수에 id 를 넘겨주며 실행을 합니다. 그리고 inner 함수에서는 해당 매개변수를 참조를 한채 남아있게 됩니다.

클로저 함수와 메모리

클로저 함수는 내부 슬롯에 렉시컬 환경을 담고 있습니다. 외부 함수의 생명주기가 다해 스택에서 내려가더라도 외부 함수의 렉시컬 환경은 자바스크립트의 GC 에 의해 처리되지 않습니다. 자바스크립트의 GC 는 참조중인 참조값들은 처리하지 않기 때문입니다. 그렇기 때문에 클로저 함수에의해 참조된 렉시컬 환경은 클로저 함수의 생명주기가 다하기 전까진 메모리에 남아있습니다.

하지만 모던 자바스크립트 엔진이 메모리 누수에 대한 걱정을 덜어줍니다. 모던 자바스크립트 엔진은 최적화가 잘 되어있기 때문에 클로저가 참조하고 있지 않은 식별자는 기억하지 않습니다. 다시 말해서 상위 스코프의 식별자 중에서 기억해야 하는 값만 기억하고 있습니다.

즉 클로저의 메모리 점유는 정말 필요한 것만 기억하기 위함 입니다.

모던 브라우저에서의 클로저

모던 브라우저에서는 메모리 최적화가 잘 되어 있습니다. 그래서 아래와 같은 케이스만 클로저 함수로 여깁니다.

  • 외부 함수의 식별자를 참조하고 있으며 외부 함수보다 오래 유지되는 경우

중첩된 함수이더라도 외부 함수보다 생명주기가 짧은 경우 클로저로 여기지 않습니다.
중첩된 함수이더라도 내부 함수가 외부함수의 식별자를 참조하지 않는 경우 클로저로 여기지 않습니다.

profile
안녕하세요! 주니어 웹 개발자입니다 😆

0개의 댓글