클로저

kimhayeon·2024년 7월 5일
0

JavaScript

목록 보기
5/9

배경지식

렉시컬 스코프란?

함수를 어디에 정의했는지에 따라 상위 스코프를 결정하는 정적 스코프를 의미한다.

렉시컬 환경이란?

  • 식별자, 식별자에 바인딩된 값, 상위 스코프에 대한 참조를 기록하는 자료구조
  • 실행 컨텍스트를 구성하는 컴포넌트
  • 스코프와 식별자를 관리
  • 렉시컬 스코프의 실체

클로저

클로저란?

생명 주기가 종료된 외부 함수의 변수를 참조하는 중첩 함수를 클로저라고 한다.

JavaScript에서 클로저가 가능한 이유는?

함수가 일급 객체이며, 렉시컬 스코프를 따르기 때문이다.

  • 렉시컬 스코프가 가능하려면 함수는 자신이 정의된 환경을 기억해야 한다. 이를 위해 내부 슬롯에 자신이 정의된 환경의 참조를 저장한다.
  • 이론적으로 JavaScript의 모든 함수는 자신이 정의된 환경을 기억하므로 클로저다. 하지만 모던 브라우저는 어떤 식별자도 참조하지 않는 상위 스코프를 기억하지 않는다. 따라서 모든 함수를 클로저라고 부르지는 않는다.

클로저 사용 예시

상태 보호

React의 useStatesetState를 통해서만 상태를 변경할 수 있다. useState 수명 주기가 끝난 후에 setState로 상태에 접근하고 변경할 수 있으므로 클로저를 기반으로 구현되어 있을 것이라고 짐작할 수 있다.

function useState(initialValue) {
  let state = initialValue;

  const getState = () => state;
  const setState = (newValue) => {
    state = newValue;
  };

  return [getState, setState];
}

외부 함수의 수명 주기와 상관 없이 상태 사용

컴포넌트가 렌더링된 후 실행되는 useEffect의 클린업 함수는 함수가 정의됐을 당시에 상태 값을 참조한다. 이를 통해 클린업 함수가 클로저임을 알 수 있다.

setTimeout & 이벤트 핸들러

setTimeout의 콜백 함수 혹은 이벤트 핸들러가 외부 데이터를 사용할 때 유용하다.

function outerFunction() {
  let message = 'Hello, world!';

  setTimeout(function innerFunction() {
    console.log(message);
  }, 1000);
}

outerFunction();

부분 적용 함수

함수에 일부 인자를 미리 전달하여 함수를 반환하는 경우에 사용할 수 있다.

const debounce = function(func, wait) {
  let timeoutId = null;
  return function (event) {
    const self = this;
    clearTimeout(timeoutId);
    timeoutId = setTimeout(func.bind(self, event), wait);
  };
}

const moveHandler = function(e) {
  console.log('move event 처리');
};

document.body.addEventListener('mousemove', debounce(moveHandler, 500));

중복 코드 줄이기

const wrap = (fn) => {
  return function() {
    const res = fn.apply(this, arguments);
    window.ReactNativeWebView.postMessage('navigationStateChange');
    return res;
  }
}

history.pushState = wrap(history.pushState);
history.replaceState = wrap(history.replaceState);

history.pushState = function() {
  const res = history.pushState.apply(this, arguments);
  window.ReactNativeWebView.postMessage('navigationStateChange');
  return res;
};

history.replaceState = function() {
  const res = history.pushState.apply(this, arguments);
  window.ReactNativeWebView.postMessage('navigationStateChange');
  return res;
};

클로저의 장단점

장점

  • 상태를 은닉하고 특정 함수에게만 상태 변경을 허용하여 상태가 의도치 않게 변경되지 않도록 관리할 수 있습니다.
  • 전역 변수 사용을 억제할 수 있습니다.

단점

  • 클로저가 필요하지 않은 외부 변수를 참조한다면 메모리를 불필요하게 잡아먹을 수 있습니다.
  • 클로저 수명을 적절히 관리하지 않으면 성능에 악영향을 미칩니다.

0개의 댓글