[TIL] Closure

혱·2023λ…„ 10μ›” 23일
1

TIL

λͺ©λ‘ 보기
12/85
post-thumbnail

ν΄λ‘œμ €

(1) ν΄λ‘œμ €μ˜ κ°œλ…

πŸ’‘ [MDN]
ν΄λ‘œμ €λŠ” ν•¨μˆ˜μ™€ κ·Έ ν•¨μˆ˜κ°€ μ„ μ–Έλœ λ ‰μ‹œμ»¬ ν™˜κ²½κ³Όμ˜ 쑰합이닀

  • λŒ€μ²΄ 무슨 말이람?
  • μ½”λ“œλ₯Ό λ³΄λ©΄μ„œ ν•¨μˆ˜κ°€ μ„ μ–Έλœ λ ‰μ‹œμ»¬ ν™˜κ²½μ΄ 무슨 말인지 μ•Œμ•„λ³΄μž.
const x = 1;

function outerFunc() {
  const x = 10;
  function innerFunc() {
    console.log(x); // 10
  }

  innerFunc();
}

outerFunc();
  • outerFunc ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ 쀑첩 ν•¨μˆ˜ innerFuncκ°€ μ •μ˜λ˜κ³  ν˜ΈμΆœλ˜μ—ˆλ‹€.
  • μ΄λ•Œ 쀑첩 ν•¨μˆ˜ innerFunc 의 μƒμœ„ μŠ€μ½”ν”„λŠ” μ™ΈλΆ€ ν•¨μˆ˜ outerFunc 의 μŠ€μ½”ν”„λ‹€.
  • λ”°λΌμ„œ μ€‘μ²©ν•¨μˆ˜ innerFuncλ‚΄λΆ€μ—μ„œ μžμ‹ μ„ ν¬ν•¨ν•˜κ³  μžˆλŠ” μ™ΈλΆ€ ν•¨μˆ˜ outerFunc 의 x λ³€μˆ˜μ— μ ‘κ·Όν•  수 μžˆλ‹€.
const x = 1;

// innerFunc()μ—μ„œλŠ” outerFunc()의 x에 μ ‘κ·Όν•  수 μ—†μ£ .
// Lexical Scopeλ₯Ό λ”°λ₯΄λŠ” ν”„λ‘œκ·Έλž˜λ° 언어이기 λ•Œλ¬Έ
function outerFunc() {
  const x = 10;
  innerFunc(); // 1
}

function innerFunc() {
  console.log(x); // 1
}

outerFunc();
  • λ§Œμ•½ innerFuncν•¨μˆ˜κ°€ outerFunc ν•¨μˆ˜μ˜ λ‚΄λΆ€μ—μ„œ μ •μ˜λœ 쀑첩 ν•¨μˆ˜κ°€ μ•„λ‹ˆλΌλ©΄ innerFunc ν•¨μˆ˜λ₯Ό outerFunc ν•¨μˆ˜μ˜ λ‚΄λΆ€μ—μ„œ ν˜ΈμΆœν•œλ‹€ ν•˜λ”λΌλ„ outerFuncν•¨μˆ˜μ˜ λ³€μˆ˜μ— μ ‘κ·Όν•  수 μ—†λ‹€.
  • outerFuncλ‚΄μ—μ„œ innerFuncκ°€ '호좜'되고 μžˆμŒμ—λ„ λΆˆκ΅¬ν•˜κ³  'μ„ μ–Έ'은 λ°–μ—μ„œ λ˜μ—ˆκΈ° λ•Œλ¬Έμ— μŠ€μ½”ν”„λ₯Ό κ³΅μœ ν•˜μ§€ μ•ŠλŠ”λ‹€!

1. λ ‰μ‹œμ»¬ μŠ€μ½”ν”„

πŸ“Œ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„

  • μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 ν•¨μˆ˜λ₯Ό μ–΄λ””μ„œ '호좜'ν–ˆλŠ”μ§€κ°€ μ•„λ‹ˆλΌ ν•¨μˆ˜λ₯Ό 어디에 'μ •μ˜'ν–ˆλŠ”μ§€μ— 따라 μƒμœ„ μŠ€μ½”ν”„λ₯Ό κ²°μ •ν•œλ‹€. 이λ₯Ό λ ‰μ‹œμ»¬ μŠ€μ½”ν”„(정적 μŠ€μ½”ν”„)라 ν•œλ‹€.
const x = 1;

function foo() {
  const x = 10;
  bar(); // bar ν•¨μˆ˜ '호좜'
}

function bar() { // bar ν•¨μˆ˜ 'μ •μ˜'
  console.log(x);
}

foo(); // 1
bar(); // 1
  • foo ν•¨μˆ˜μ™€ bar ν•¨μˆ˜λŠ” λͺ¨λ‘ μ „μ—­μ—μ„œ μ •μ˜λœ ν•¨μˆ˜λ‹€.
  • ν•¨μˆ˜μ˜ μƒμœ„ μŠ€μ½”ν”„λŠ” ν•¨μˆ˜λ₯Ό μ–΄λ””μ„œ μ •μ˜ν–ˆλŠλƒμ— 따라 κ²°μ •λ˜λ―€λ‘œ fooν•¨μˆ˜μ™€ barν•¨μˆ˜μ˜ μƒμœ„ μŠ€μ½”ν”„λŠ” "μ „μ—­" 이닀.
  • ν•¨μˆ˜λ₯Ό μ–΄λ””μ„œ ν˜ΈμΆœν•˜λŠ”μ§€λŠ” ν•¨μˆ˜μ˜ μƒμœ„μŠ€μ½”ν”„ 결정에 μ–΄λ–€ 영ν–₯도 주지 λͺ»ν•œλ‹€.
  • 즉, ν•¨μˆ˜μ˜ μƒμœ„ μŠ€μ½”ν”„λŠ” ν•¨μˆ˜λ₯Ό μ •μ˜ν•œ μœ„μΉ˜μ— μ˜ν•΄ μ •μ μœΌλ‘œ κ²°μ •λ˜κ³  λ³€ν•˜μ§€ μ•ŠλŠ”λ‹€.

2. ν•¨μˆ˜ 객체의 λ‚΄λΆ€ 슬둯 [[Environment]]

  • μœ„ 예제처럼 ν•¨μˆ˜κ°€ μ •μ˜λœ ν™˜κ²½(μœ„μΉ˜)와 호좜된 ν™˜κ²½(μœ„μΉ˜)λŠ” λ‹€λ₯Ό 수 μžˆλ‹€.
  • λ”°λΌμ„œ ν˜ΈμΆœλ˜λŠ” ν™˜κ²½κ³Ό 관계 없이 μ •μ˜λœ ν™˜κ²½, 즉 μƒμœ„ μŠ€μ½”ν”„(ν•¨μˆ˜ μ •μ˜κ°€ μœ„μΉ˜ν•˜λŠ” μŠ€μ½”ν”„)λ₯Ό κΈ°μ–΅ν—€μ•Ό ν•œλ‹€.
  • 이λ₯Ό μœ„ν•΄ ν•¨μˆ˜λŠ” μžμ‹ μ˜ λ‚΄λΆ€ 슬둯 [[Environmnet]]에 μžμ‹ μ΄ μ •μ˜λœ ν™˜κ²½, 즉 μƒμœ„ μŠ€μ½”ν”„μ˜ μ°Έμ‘°λ₯Ό μ €μž₯ν•œλ‹€.
const x = 1;

function foo() {
  const x = 10;

  // μƒμœ„ μŠ€μ½”ν”„λŠ” ν•¨μˆ˜ μ •μ˜ ν™˜κ²½(μœ„μΉ˜)에 따라 κ²°μ •λœλ‹€.
  // ν•¨μˆ˜ 호좜 μœ„μΉ˜μ™€ μƒμœ„ μŠ€μ½”ν”„λŠ” μ•„λ¬΄λŸ° 관계가 μ—†λ‹€.
  bar();
}

// ν•¨μˆ˜ barλŠ” μžμ‹ μ˜ μƒμœ„ μŠ€μ½”ν”„, 즉 μ „μ—­ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ [[Environment]]에 μ €μž₯ν•˜μ—¬ κΈ°μ–΅ν•œλ‹€.
function bar() {
  console.log(x);
}

foo();
bar();

3. ν΄λ‘œμ €μ™€ λ ‰μ‹œμ»¬ ν™˜κ²½

  • μ™ΈλΆ€ ν•¨μˆ˜λ³΄λ‹€ 쀑첩 ν•¨μˆ˜κ°€ 더 였래 μœ μ§€λ˜λŠ” 경우, μ€‘μ²©ν•¨μˆ˜λŠ” 이미 생λͺ… μ£ΌκΈ°κ°€ μ’…λ£Œν•œ μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ³€μˆ˜λ₯Ό μ—¬μ „νžˆ μ°Έμ‘°ν•  수 μžˆλ‹€!
  • 이 κ°œλ…μ—μ„œ μ€‘μ²©ν•¨μˆ˜κ°€ λ°”λ‘œ ν΄λ‘œμ € !
const x = 1;

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

const innerFunc = outer();
innerFunc();
  • outer ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ μ€‘μ²©ν•¨μˆ˜ inner λ₯Ό λ°˜ν™˜ν•˜κ³  생λͺ…μ£ΌκΈ°λ₯Ό λ§ˆκ°ν•œλ‹€.
  • 즉, outer의 싀행이 μ’…λ£Œλ˜λ©΄ outer ν•¨μˆ˜μ˜ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλŠ” μ‹€ν–‰ μ»¨ν…μŠ€νŠΈ μŠ€νƒμ—μ„œ 제거(pop) λœλ‹€.
  • μ΄λ•Œ outer ν•¨μˆ˜μ˜ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈκ°€ μ œκ±°λ˜μ—ˆμœΌλ―€λ‘œ outer ν•¨μˆ˜μ˜ μ§€μ—­λ³€μˆ˜ x λ˜ν•œ 생λͺ… μ£ΌκΈ°λ₯Ό λ§ˆκ°ν•œλ‹€. λ”°λΌμ„œ outer ν•¨μˆ˜μ˜ μ§€μ—­λ³€μˆ˜ xλŠ” λ”λŠ” μœ νš¨ν•˜μ§€ μ•Šκ²Œ λ˜μ–΄ x λ³€μˆ˜μ— μ ‘κ·Όν•  방법은 달리 없어보인닀.
  • κ·ΈλŸ¬λ‚˜ μœ„ μ½”λ“œμ˜ μ‹€ν–‰ κ²°κ³ΌλŠ” outer ν•¨μˆ˜μ˜ 지역 λ³€μˆ˜ x의 값인 10 이닀.
  • inner ν•¨μˆ˜κ°€ innerFunc에 μ „λ‹¬λ˜μ—ˆλŠ”λ°, inner ν•¨μˆ˜λŠ” outer ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ "μ—¬μ „νžˆ" μ°Έμ‘°ν•˜κ³  μžˆλ‹€.
  • 즉, outer μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλŠ” μ‹€ν–‰ μ»¨ν…μŠ€νŠΈ μŠ€νƒμ—μ„œ 제거(pop) λ˜μ§€λ§Œ, outer ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½κΉŒμ§€ μ†Œλ©Έν•˜λŠ” 것은 μ•„λ‹ˆλ‹€. κ·ΈλŸ¬λ―€λ‘œ outer ν•¨μˆ˜μ˜ μ§€μ—­λ³€μˆ˜ x μ°Έμ‘° κ°€λŠ₯ν•˜λ‹€.
  • cf) 이것이 κ°€λŠ₯ν•œ 이유 : λ˜‘λ˜‘ν•œ 가비지 μ»¬λ ‰ν„°λŠ” μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” κ²ƒλ§Œ κ°€μ Έκ°€λŠ”λ°, outer ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ€ μ°Έμ‘°ν•˜λŠ” 곳이 μžˆμœΌλ‹ˆ λ†”λ‘λŠ” 것이닀!

(2) ν΄λ‘œμ €μ˜ ν™œμš©

  • ν΄λ‘œμ €λŠ” μƒνƒœλ₯Ό μ•ˆμ „ν•˜κ²Œ λ³€κ²½ν•˜κ³  μœ μ§€ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•œλ‹€.
  • μƒνƒœκ°€ μ˜λ„μΉ˜ μ•Šκ²Œ λ³€κ²½λ˜μ§€ μ•Šλ„λ‘ μƒνƒœλ₯Ό μ•ˆμ „ν•˜κ²Œ μ€λ‹‰ν•˜κ³ , νŠΉμ • ν•¨μˆ˜μ—κ²Œλ§Œ μƒνƒœ 변경을 ν—ˆμš©ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•œλ‹€.
// 카운트 μƒνƒœ λ³€κ²½ ν•¨μˆ˜ #1
// ν•¨μˆ˜κ°€ 호좜될 λ•Œλ§ˆλ‹€ 호좜된 횟수λ₯Ό λˆ„μ ν•˜μ—¬ 좜λ ₯ν•˜λŠ” μΉ΄μš΄ν„°λ₯Ό κ΅¬ν˜„ν•΄μš”!

// 카운트 μƒνƒœ λ³€μˆ˜
let num = 0;

// 카운트 μƒνƒœ λ³€κ²½ ν•¨μˆ˜
const increase = function () {
  // 카운트 μƒνƒœλ₯Ό 1만큼 μ¦κ°€μ‹œν‚¨λ‹€.
  return ++num;
};

console.log(increase()); // 1
num = 100; // 치λͺ…적인 단점이 μžˆμ–΄μš”. 정보 접근을 막을 수 μ—†μ–΄μš”.
console.log(increase()); // 101
console.log(increase()); // 102
  • 보완해야 ν•  사항
    • 카운트의 μƒνƒœ(num λ³€μˆ˜μ˜ κ°’) 은 increase()κ°€ 호좜되기 μ „κΉŒμ§€ λ³€κ²½λ˜μ§€ μ•Šκ³  μœ μ§€λ˜μ–΄μ•Ό ν•œλ‹€.
    • 이λ₯Ό μœ„ν•΄ 카운트의 μƒνƒœ(num λ³€μˆ˜μ˜ κ°’)은 increase()만 λ³€κ²½ν•  수 μžˆμ–΄μ•Ό ν•œλ‹€.
    • μ „μ—­λ³€μˆ˜ numdmf increase() ν•¨μˆ˜μ˜ μ§€μ—­λ³€μˆ˜λ‘œ λ°”κΎΈμ–΄λ³΄μž.
// 카운트 μƒνƒœ λ³€κ²½ ν•¨μˆ˜ #2
const increase = function () {
  // 카운트 μƒνƒœ λ³€μˆ˜
  let num = 0;

  // 카운트 μƒνƒœλ₯Ό 1만큼 μ¦κ°€μ‹œν‚¨λ‹€.
  return ++num;
};

// 이전 μƒνƒœκ°’μ„ μœ μ§€ λͺ»ν•¨
console.log(increase()); //1
console.log(increase()); //1
console.log(increase()); //1
  • 보완해야 ν•  사항
    • μ „μ—­λ³€μˆ˜ num을 increase() ν•¨μˆ˜μ˜ μ§€μ—­λ³€μˆ˜λ‘œ λ³€κ²½ν•˜μ—¬ μ˜λ„μΉ˜ μ•Šμ€ μƒνƒœ 변경은 λ°©μ§€ν–ˆλ‹€. 이제 num λ³€μˆ˜μ˜ μƒνƒœλŠ” increase() ν•¨μˆ˜λ§Œ λ³€κ²½ν•  수 μžˆλ‹€.
    • ν•˜μ§€λ§Œ increase() ν•¨μˆ˜κ°€ 호좜될 λ•Œλ§ˆλ‹€ μ§€μ—­λ³€μˆ˜ num이 μ΄ˆκΈ°νšŒλœλ‹€.
    • 즉, μƒνƒœκ°€ λ³€κ²½λ˜κΈ° 이전 μƒνƒœλ₯Ό μœ μ§€ν•˜μ§€ λͺ»ν•œλ‹€.
    • 이제 ν΄λ‘œμ €λ₯Ό μ‚¬μš©ν•΄ 보자.
// 카운트 μƒνƒœ λ³€κ²½ ν•¨μˆ˜ #3
const increase = (function () {
  // 카운트 μƒνƒœ λ³€μˆ˜
  let num = 0;

  // ν΄λ‘œμ €
  return function () {
    return ++num;
  };
}());

// 이전 μƒνƒœκ°’μ„ μœ μ§€
console.log(increase()); //1
console.log(increase()); //2
console.log(increase()); //3
  • μœ„ μ½”λ“œκ°€ μ‹€ν–‰λ˜λ©΄ μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜κ°€ 호좜되고, μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜κ°€ λ°˜ν™˜ν•œ ν•¨μˆ˜κ°€ increase λ³€μˆ˜μ— ν• λ‹Ήλœλ‹€.
  • increase λ³€μˆ˜μ— ν• λ‹Ήλœ ν•¨μˆ˜λŠ” μžμ‹ μ΄ μ •μ˜λœ μœ„μΉ˜μ— μ˜ν•΄ κ²°μ •λœ μƒμœ„ μŠ€μ½”ν”„μΈ μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ κΈ°μ–΅ν•˜λŠ” ν΄λ‘œμ €λ‹€.
  • μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜λŠ” 호좜된 이후 μ†Œλ©Έλ˜μ§€λ§Œ, μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜κ°€ λ°˜ν™˜ν•œ ν΄λ‘œμ €λŠ” increase λ³€μˆ˜μ— ν• λ‹Ήλ˜μ–΄ ν˜ΈμΆœλœλ‹€.
  • μ΄λ•Œ μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜κ°€ λ°˜ν™˜ν•œ ν΄λ‘œμ €λŠ” μžμ‹ μ΄ μ •μ˜λœ μœ„μΉ˜μ— μ˜ν•΄ κ²°μ •λœ μƒμœ„ μŠ€μ½”ν”„μΈ μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ κΈ°μ–΅ν•˜κ³  μžˆλ‹€.
  • λ”°λΌμ„œ μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜κ°€ λ°˜ν™˜ν•œ ν΄λ‘œμ €λŠ” 카운트 μƒνƒœλ₯Ό μœ μ§€ν•˜κΈ° μœ„ν•œ 자유 λ³€μˆ˜ num을 μ–Έμ œ μ–΄λ””μ„œ ν˜ΈμΆœν•˜λ“ μ§€ μ°Έμ‘°ν•˜κ³  λ³€κ²½ν•  수 μžˆλ‹€.

πŸ“Œ κ²°λ‘ 
ν΄λ‘œμ €λŠ” μƒνƒœ(state)κ°€ μ˜λ„μΉ˜ μ•Šκ²Œ λ³€κ²½λ˜μ§€ μ•Šλ„λ‘ μ•ˆμ „ν•˜κ²Œ 은닉(information hiding) ν•˜κ³  νŠΉμ • ν•¨μˆ˜μ—κ²Œλ§Œ μƒνƒœ 변경을 ν—ˆμš©ν•˜μ—¬ μƒνƒœλ₯Ό μ•ˆμ „ν•˜κ²Œ λ³€κ²½ν•˜κ³  μœ μ§€ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•œλ‹€.

profile
λŠλ¦¬λ”λΌλ„ μ‘°κΈˆμ”©, κΎΈμ€€νžˆ

0개의 λŒ“κΈ€