JS 클로저

gang_shik·2025년 3월 19일
0

JavaScript

목록 보기
18/23

클로저란?

// 아래의 경우 type은 return을 문자열을 하기에 string으로 나옴
function returnX() {
  return 'x';
}

const x = returnX()

typeof x // string

// 아래의 경우에서는 type은 function임 왜냐하면 return을 function 블럭 자체를 하기 떄문임
function returnX() {
  let x = 'x';

  return function returnY() {
    return x + 'y';
  };
}

const x = returnX();

typeof x; // function

// 아래의 경우는 type은 string 왜냐하면 X함수 호출 또 Y함수를 호출하기 떄문임
const x = returnX()()

typeof x; // string
function returnChar(x) {
  let outerChar = x;

  return function returnChar2(y) {
    let innerChar = y;

    return outerChar + innerChar;
  };
}

// x라는 시점을 기억하고 있음
const x = returnChar('x'); // x
const xy = x('y'); // xy
const xz = x('z'); // xz
const xc = x('c'); // xc
  • 시점을 기억, 바인딩한다는 느낌은 아래의 예시도 마찬가지임
function sum(num1) {
  return function(num2) {
    return num1 + num2;
  };
}

// num1의 값 5를 계속 기억하고 있음
const sum5 = sum(5); // 5
sum5(10); // 15 => num1의 값 5를 계속 기억하고 있음
sum5(20); // 25
sum5(30); // 35

// 마찬가지로 10이 바인딩
const sum10 = sum(10); // 10
sum10(5); // 15
sum10(15); // 25
function sum(num1) {
  return function(num2) {
    return function(num3) {
      return num1 + num2 + num3;
    };
  };
}

// 마찬가지로 num1에 바인딩이 되어 있는 것
const sum5 = sum(5);
const sum10 = sum(10);

// num1만 바인딩 되어 있으므로, num2, num3 더한 값으로 계산
sum5(10)(10); // 25
sum5(20)(10); // 35
sum5(30)(10); // 45

// num1만 바인딩 되어 있으므로, num1 + num2 계산
sum10(5); // 15
sum10(15); // 25
  • 클로저는 이런식으로 어휘적인 환경을 기억시켜 놓을 수 있는 함수임
  • 위를 화살표 함수로도 줄여 쓸 수 있음
const sum => (num1) => (num2) => (num3) => num1 + num2 + num3;

// 마찬가지로 num1에 바인딩이 되어 있는 것
const sum5 = sum(5);
const sum10 = sum(10);

// num1만 바인딩 되어 있으므로, num2, num3 더한 값으로 계산
sum5(10)(10); // 25
sum5(20)(10); // 35
sum5(30)(10); // 45

// num1만 바인딩 되어 있으므로, num1 + num2 계산
sum10(5); // 15
sum10(15); // 25
  • 결국, 클로저는 이전 환경을 기억해두고, 캡쳐시켜놓을 수 있는 함수 기법임, 클로저를 억지로 쓸 필요는 없지만, 이에 대해서 활용한 케이스를 생각하면 됨

은닉화

  • IIFE를 활용해서 은닉화를 한 것을 기억할 것임, IIFE를 통해서 즉시실행함수 개념보다, 이를 일종의 스코프로 쓴다고 생각하면 좋음
  • 이는 함수도 마찬가지임, 아래의 예시치럼 함수 내부의 값에 접근을 못하고 바로 실행을 시켜야함
function a() {
  let temp = 'a';

  return temp;
}

const result = a(); // a.temp로 값 접근 불가
result; // 실행을 시켜야만 temp 값을 확인함
  • 이럴 때 아래와 같이 클로저를 활용해서 앞서 은닉화 된 값을 접근하여 볼 수 있는 것
function privateData() {
  let temp = 'a';

  return {
    value: function() {
      return temp;
    },
  };
}

// temp는 함수 스코프내에 있어서 접근이 안됐지만 접근해서 값을 볼 수 있음
const private = privateData()
private.value()

// 값을 바꿀 수도 있음
function privateData() {
  let temp = 'a';

  return {
    value: function() {
      return temp;
    },
    changeValue: function (newVal) {
      temp = newVal;
    },
  };
}

const private = privateData()
private.value() // a(기존값)
private.changeValue('b'); // temp 값을 바꿈
private.value() // 값이 b로 바뀜
// 여기서 새로운 privateData를 만들어도 그 값은 유지가 됨
const private2 = privateData()
private2.value() // a(다시 기존값)
  • 예시로 카운터 앱을 만들어서 테스트 해 봄
function CounterApp(initValue) {
  let countValue = initValue ?? 0;

  return {
    value: function() {
      return countValue;
    },
    increment: function () {
      countValue++;
    },
    decrement: function () {
      countValue--;
    },
  };
}

const counter1 = CounterApp(1);
const counter2 = CounterApp(2);

counter1.value(); // 1
counter2.value(); // 2

counter1.increment();
counter1.increment();
counter1.increment();
counter1.increment();

counter1.value(); // 5
counter2.value(); // 2 초기값을 유지하고 있음
  • 이를 과거에는 모듈 패턴이라고 불렀음, 클로저를 이용해서 각자의 어휘적 환경, private 데이터를 넣어두고 그 데이터를 바꿔주는 함수를 넣어줌(마치 흡사 getter, setter를 쓰듯이)
  • this도 없는데 초기값이 다른 것을 각자의 스코프가 유지되고 그 스코프에 따라 계산 및 처리가 됨, 이런식으로 클로저를 썼음
  • 함수안에 값을 숨기고 싶을때 클로저를 쓸 수 있다고 알아두면 좋음

활용 사례

  • debounce 이벤트, 이벤트 실행시 과하게 실행되는 것을 지연을 하는 기법, 구간 스크롤, 클릭 여러번 시 지연시킬 떄 주로 씀
  • 이럴 때 클로저를 많이 씀, 아래와 같이 클릭이 되는 시점의 함수를 기억하고 있다고 이벤트가 실행 되는 시점에 실행을 시키는 것
  • 아래와 같을 때 클로저를 가장 많이씀
buttonElement.addEventListener(
  'click',
  debounce(handleClick, 500),
);

function debounce(func, timeout = 300) {
  let timer;

  return (...args) => {
    clearTimeout(timer);

    timer = setTimeout(() => { func.apply(this, args); },
  };
}
profile
측정할 수 없으면 관리할 수 없고, 관리할 수 없으면 개선시킬 수도 없다

0개의 댓글