Closure

KanDohyung·2024년 12월 14일

개념정리

목록 보기
14/28

함수가 선언될때 렉시컬 환경을 기억하는 기능
회부 함수의 스코프의 변수에 접근할 수 있는 기능

동작원리

  1. 함수의 정의와 실행
    • 함수 호출시 JavaScript는 실행 컨택스트를 생성함
    • 실행 컨텍스트에는 함수의 변수 스코프와 렉시컬 환경이 포함됨
  2. 클로저와 메모리의 유지
    • 클로저로 외부 함수의 변수에 접근하는 경우, JavaScript는 외부 함수의 스코프를 메모리에 유지함

주요 특징

  • 외부 스코프 접근 가능
  • 스코프 유지
  • 캡슐화
const outerFunc = () => {
		let x = 10; // '자유 변수' 라고 한다.

		// 클로저
		const innerFunc = (y) => {
				x = x + y;
				console.log(x);
		}

		return innerFunc;
}

const addFunc = outerFunc();
addFunc(5); // 15
addFunc(10); // 25
  • x의 값은 클로저로 접근하여 메모리에 저장되어 있음
  • addFunc가 리턴하는 것은 innerFunc이므로 매개변수를 받을 수 있음

장점

  • 데이터 은닉(캡슐화)
  • 모듈화

단점

  • 메모리 누수 : 클로저는 참조를 유지하므로 외부 변수를 계속 참조하면 메모리 누수가 발생할 수 있음. → 사용하지 않을때는 반드시 참조를 제거해야함
  • 디버깅 어려움 : 클로저에 익숙하지 않은 경우 디버깅이 어려움
function createClosure() {
  let largeData = new Array(1000000).fill("data");

  return function () {
    console.log(largeData.length);
  };
}

const closure = createClosure();
closure(); // 1000000

// 메모리 해제
closure = null;

클로저의 활용

정보 은닉

  • 내부 로직의 노출 최소화
  • 클로저를 통해 JavaScript에서도 클래스 기반 언어의 public과 private과 유사하게 변수에 대한 접근 권한을 제어할 수 있음
const user = () => {
		let userName = ''; // private

		return {
				getUserName: function() {
						return userName;
				},
				setUserName: function(_userName) {
						userName = _userName;
				}
		};
}

const user1 = user();
user1.getUserName(); // ''

user1.setUserName('Chojs');
user1.getUserName(); // Chojs

부분 적용 함수

  • n개의 인자를 받는 함수에 따라 미리 m개의 인자만 넘겨 기억 시키고, 나중에 나머지 인자를 넘겨 원래 함수의 실행 결과를 얻을 수 있도록 하는 함수
  • debounce가 대표적
    여러 번 발생하는 이벤트에서, 가장 마지막 이벤트만을 실행되도록 만드는 개념. 예를 들어, mousemove 이벤트 핸들러는 mouse가 움직일 때 마다 실행되는데, 디바운스를 적용시켜 mouse의 움직임이 끝날 때 핸들러를 한번 호출하여 과도한 호출을 방지함으로써 퍼포먼스를 향상시킬 수 있다.
const debounce = (eventName, func, wait) => {
		let timerId = null;
		
		return function(event) {
				let self = this;
				clearTime(timerId);
				timerId = setTimeout(func.bind(self, event), wait);
		};
};

const mousemoveHandler = () => {
		console.log('wait 시간 내 많은 이벤트 발생 시 한번만 호출됨');
}

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

커링

  • 단일 호출로 처리하는 함수를 각각의 인수가 호출 가능한 프로세스로 호출된 후 병합되도록 변환하는 것
  • 주로 지연실행에 사용됨. 함수의 마지막 인자가 들어올때까지 지연시킨 후 마지막 인자가 들어왔을 때 실행됨
// 커링 함수
const curry = (func) => (a) => (b) => func(a, b);

const sum = (a, b) => (a + b);

let curriedSum = curry(sum);
console.log(curriedSum(1)(2)); // 3
  • 로그 함수 구현
const log = (date) => (importance) => (message) => {
		return `[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`;
}

function log(date) {
    return function(importance) {
        return function(message) {
            return `[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`;
        };
    };
}

// 기본
console.log(log(new Date())("WARNING")("Reference Error!!!")); // '[15:54] [WARNING] Reference Error!!!'

// 응용
const logNow = log(new Date());
console.log(logNow("INFO")("message")); // '[16:51] [INFO] message'

const debugNow = logNow("DEBUG");
console.log(debugNow("message")); // '[16:59] [DEBUG] message'
  • logNow 내부의 값은 유동적으로 바뀌지만 함수로서 제어되니 문제가 없음
  • debugNow 처럼 사용자의 입력으로 변경되는 값을 제어할 때는 추가적인 처리 필요

0개의 댓글