πŸ“ŒLanguage/JavaScript

[Deep Dive] ν΄λ‘œμ €μ™€ λ ‰μ‹œμ»¬μŠ€μ½”ν”„

hellohailie 2022. 12. 12. 15:44

βœ”οΈ 이번 정독을 톡해 μ•Œκ²Œ 된 것
ν΄λ‘œμ €λŠ” λ¬΄μ—‡μΈκ°€μš”?
λ ‰μ‹œμ»¬ μŠ€μ½”ν”„κ°€ λ¬΄μ—‡μΈκ°€μš”?

 

<λ‹΅ 미리보기>

πŸ€” ν΄λ‘œμ €λŠ” λ¬΄μ—‡μΈκ°€μš”?

πŸ€“ ν΄λ‘œμ €λŠ” ν•¨μˆ˜μ™€ κ·Έ ν•¨μˆ˜κ°€ μ„ μ–Έλœ λ ‰μ‹œμ»¬ ν™˜κ²½κ³Όμ˜ μ‘°ν•©μž…λ‹ˆλ‹€. 

 

πŸ€” λ ‰μ‹œμ»¬ μŠ€μ½”ν”„κ°€ λ¬΄μ—‡μΈκ°€μš”?

πŸ€“ μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 ν•¨μˆ˜λ₯Ό 어디에 μ •μ˜ν–ˆλŠ”지에 따라 μƒμœ„ μŠ€μ½”ν”„λ₯Ό κ²°μ •ν•˜λŠ”λ° 이λ₯Ό λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λΌκ³  ν•©λ‹ˆλ‹€. 

 


βœ”οΈ ν΄λ‘œμ €λž€

ν΄λ‘œμ €λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ 고유의 κ°œλ…μ΄ μ•„λ‹ˆλ‹€. ν•¨μˆ˜λ₯Ό 일급 객체둜 μ·¨κΈ‰ν•˜λŠ” ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄μ—μ„œ μ‚¬μš©λ˜λŠ” νŠΉμ„±μ΄λ‹€. 

 

ν΄λ‘œμ € in MDN

πŸ‘‰ A closure is the combination of a function and the lexical environment within which that function was declared.

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

 

const x = 1;

function outerFunc() {
  const x = 10;

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

  innerFunc();
}

outerFunc();

→ outer ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ 쀑첩 ν•¨μˆ˜(inner)κ°€ μ •μ˜λ˜κ³  ν˜ΈμΆœλ˜μ—ˆλ‹€. 

μ΄λ•Œ inner ν•¨μˆ˜μ˜ μƒμœ„ μŠ€μ½”ν”„λŠ” μ™ΈλΆ€ ν•¨μˆ˜ outer ν•¨μˆ˜μ˜ μŠ€μ½”ν”„μ΄λ‹€. λ”°λΌμ„œ inner λ‚΄λΆ€μ—μ„œ μžμ‹ μ„ ν¬ν•¨ν•˜κ³  μžˆλŠ” μ™ΈλΆ€ ν•¨μˆ˜ outer ν•¨μˆ˜μ˜ x λ³€μˆ˜μ— μ ‘κ·Όν•  수 μžˆλ‹€. 

 

=> μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λ₯Ό λ”°λ₯΄λŠ” ν”„λ‘œκ·Έλž˜λ° 언어이닀. 

 


βœ”οΈ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λž€

μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 ν•¨μˆ˜λ₯Ό μ–΄λ””μ„œ ν˜ΈμΆœν–ˆλŠ”μ§€κ°€ μ•„λ‹ˆλΌ ν•¨μˆ˜λ₯Ό 어디에 μ •μ˜ν–ˆλŠ”μ§€μ— 따라 μƒμœ„ μŠ€μ½”ν”„λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€. 

 

λ ‰μ‹œμ»¬ ν™˜κ²½μ˜ 'μ™ΈλΆ€ λ ‰μ‹œμ»¬ ν™˜κ²½μ— λŒ€ν•œ μ°Έμ‘°'에 μ €μž₯ν•  μ°Έμ‘°κ°’, 즉 μƒμœ„ μŠ€μ½”ν”„μ— λŒ€ν•œ μ°Έμ‘°λŠ” ν•¨μˆ˜ μ •μ˜κ°€ ν‰κ°€λ˜λŠ” μ‹œμ μ— ν•¨μˆ˜κ°€ μ •μ˜λœ ν™˜κ²½μ— μ˜ν•΄ κ²°μ •λ©λ‹ˆλ‹€. 

 

(... μ΄ν•΄λŠ” ν–ˆμ§€λ§Œ λ‚¨μ—κ²Œ μ„€λͺ…ν• λ§ŒνΌ λͺ…ν™•ν•˜κ²Œ μ΄ν•΄λŠ” λͺ»ν•¨...)

 


βœ”οΈ ν•¨μˆ˜ 객체의 λ‚΄λΆ€ 슬둯

ν•¨μˆ˜κ°€ μ •μ˜λœ ν™˜κ²½(μœ„μΉ˜)κ³Ό ν˜ΈμΆœλ˜λŠ” ν™˜κ²½(μœ„μΉ˜)λŠ” λ‹€λ₯Ό 수 μžˆλ‹€.

κ·Έλž˜μ„œ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„κ°€ κ°€λŠ₯ν•˜λ €λ©΄ ν•¨μˆ˜λŠ” μžμ‹ μ΄ μ •μ˜λœ ν™˜κ²½, 즉 μƒμœ„ μŠ€μ½”ν”„(ν•¨μˆ˜ μ •μ˜κ°€ μœ„μΉ˜ν•˜λŠ” μŠ€μ½”ν”„)λ₯Ό κΈ°μ–΅ν•΄μ•Ό ν•œλ‹€. 이λ₯Ό μœ„ν•΄μ„œ ν•¨μˆ˜λŠ” μžμ‹ μ˜ λ‚΄λΆ€ μŠ¬λ‘―μ— μžμ‹ μ΄ μ •μ˜λœ ν™˜κ²½, 즉 μƒμœ„ μŠ€μ½”ν”„μ˜ μ°Έμ‘°λ₯Ό μ €μž₯ν•œλ‹€. 

 

const x = 1;

function foo() {
  const x = 10;

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

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

foo(); // 1
bar(); // 1

 

 


βœ”οΈ ν΄λ‘œμ €μ™€ λ ‰μ‹œμ»¬ ν™˜κ²½

const x = 1;

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

// outer ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ 쀑첩 ν•¨μˆ˜ innerλ₯Ό λ°˜ν™˜ν•œλ‹€.
// 그리고 outer ν•¨μˆ˜μ˜ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλŠ” μ‹€ν–‰ μ»¨ν…μŠ€νŠΈ μŠ€νƒμ—μ„œ νŒλ˜μ–΄ μ œκ±°λœλ‹€.
const innerFunc = outer(); 
innerFunc(); // 10

outer ν•¨μˆ˜λŠ” inner ν•¨μˆ˜λ₯Ό λ¦¬ν„΄ν•˜κ³  μ‹€ν–‰ μ»¨ν…μŠ€νŠΈ μŠ€νƒμ—μ„œ μ œκ±°λœλ‹€. μ™œλƒλ©΄ 생λͺ…μ£ΌκΈ°κ°€ λ§ˆκ°λ˜μ—ˆκΈ° λ•Œλ¬Έμ—!

κ·Έλž˜μ„œ 얼핏보기에 outerν•¨μˆ˜μ˜ μ§€μ—­λ³€μˆ˜ xκ°€ μœ νš¨ν•˜μ§€ μ•Šμ€ κ²ƒμ²˜λŸΌ 보일 수 μžˆλŠ”λ° 싀은 μ•„λ‹ˆλ‹€!

 

μ™œλƒλ©΄ 

 

 

μ™ΈλΆ€ν•¨μˆ˜λ³΄λ‹€ 쀑첩 ν•¨μˆ˜κ°€ 더 였래 μœ μ§€λ˜λŠ” 경우,
μ€‘μ²©ν•¨μˆ˜λŠ” 이미 생λͺ… μ£ΌκΈ°κ°€ μ’…λ£Œν•œ μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ³€μˆ˜λ₯Ό μ°Έμ‘°ν•  수 있기 λ•Œλ¬Έμ΄λ‹€. 
(이게 λ°”λ‘œ ν΄λ‘œμ € closure)

 

outerν•¨μˆ˜μ˜ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλŠ” μ‹€ν–‰ μ»¨ν…μŠ€νŠΈ μŠ€νƒμ—μ„œ μ œκ±°λ˜μ§€λ§Œ outer ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½κΉŒμ§€ μ†Œλ©Έν•˜λŠ”κ±΄ μ•„λ‹ˆλ‹€!!

 

outer ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ˜ inner ν•¨μˆ˜μ˜ λ‚΄λΆ€ μŠ¬λ‘―μ— μ˜ν•΄ 참쑰되고 있고, inner ν•¨μˆ˜λŠ” μ „μ—­λ³€μˆ˜ innerFunc에 μ˜ν•΄ 참쑰되고 μžˆμœΌλ―€λ‘œ 가비지 μ»¬λ ‰μ…˜μ˜ λŒ€μƒμ΄ λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€. 

(가비지 μ»¬λ ‰ν„°λŠ” λˆ„κ΅°κ°€ μ°Έμ‘°ν•˜κ³  μžˆλŠ” λ©”λͺ¨λ¦¬ 곡간을 ν•¨λΆ€λ‘œ ν•΄μ œν•˜μ§€ μ•ŠλŠ”λ‹€. )

 

 


βœ”οΈ ν΄λ‘œμ €κ°€ μ•„λ‹Œ ν•¨μˆ˜

μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ λͺ¨λ“  ν•¨μˆ˜λŠ” μƒμœ„ μŠ€μ½”ν”„λ₯Ό κΈ°μ–΅ν•˜λ―€λ‘œ 이둠적으둜 λͺ¨λ“  ν•¨μˆ˜λŠ” ν΄λ‘œμ €μ΄λ‹€. ν•˜μ§€λ§Œ 일반적으둜 λͺ¨λ“  ν•¨μˆ˜λ₯Ό ν΄λ‘œμ €λΌκ³  ν•˜μ§€ μ•ŠλŠ”λ‹€. 

 

1. μƒμœ„ μŠ€μ½”ν”„μ˜ μ‹λ³„μžλ₯Ό μ°Έμ‘°ν•˜μ§€ μ•ŠμœΌλ©΄ ν΄λ‘œμ €κ°€ μ•„λ‹ˆλ‹€.

2. μ™ΈλΆ€ν•¨μˆ˜λ³΄λ‹€ μ€‘μ²©ν•¨μˆ˜μ˜ 생λͺ…μ£ΌκΈ°κ°€ 짧으면 κ·Έ μ€‘μ²©ν•¨μˆ˜λŠ” ν΄λ‘œμ €κ°€ μ•„λ‹ˆλ‹€.

 

 

ν΄λ‘œμ €λŠ” 쀑첩 ν•¨μˆ˜κ°€ μƒμœ„ μŠ€μ½”ν”„μ˜ μ‹λ³„μžλ₯Ό μ°Έμ‘°ν•˜κ³  있고,
쀑첩 ν•¨μˆ˜κ°€ μ™ΈλΆ€ ν•¨μˆ˜λ³΄λ‹€ 더 였래 μœ μ§€λ˜λŠ” κ²½μš°μ— ν•œμ •ν•˜λŠ” 것이 μΌλ°˜μ μ΄λ‹€. 


βœ”οΈ ν΄λ‘œμ €μ˜ 단점?!

ν΄λ‘œμ €λŠ” μƒμœ„ μŠ€μ½”ν”„λ₯Ό κΈ°μ–΅ν•΄μ•Ό ν•˜λ―€λ‘œ λΆˆν•„μš”ν•œ λ©”λͺ¨λ¦¬μ˜ 점유λ₯Ό κ±±μ •ν•  μˆ˜λ„ μžˆκ² μ§€λ§Œ,

λͺ¨λ˜ μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 μ΅œμ ν™”κ°€ 잘 λ˜μ–΄ μžˆμ–΄μ„œ ν΄λ‘œμ €κ°€ μ°Έμ‘°ν•˜κ³  μžˆμ§€ μ•ŠλŠ” μ‹λ³„μžλŠ” κΈ°μ–΅ν•˜μ§€ μ•ŠλŠ”λ‹€κ³  ν•œλ‹€. 

 

κ·Έλž˜μ„œ ν΄λ‘œμ €μ˜ λ©”λͺ¨λ¦¬ μ μœ μ— λŒ€ν•΄ κ±±μ •ν•  ν•„μš” μ—†κ³ , ν•„μš”ν•˜λ‹€λ©΄ ν΄λ‘œμ €λ₯Ό 적극 ν™œμš©ν•΄λ³΄μž!!


βœ”οΈ ν΄λ‘œμ €μ˜ ν™œμš©

ν΄λ‘œμ €λŠ” μƒνƒœκ°€ μ˜λ„μΉ˜ μ•Šκ²Œ λ³€κ²½λ˜μ§€ μ•Šλ„λ‘ μƒνƒœλ₯Ό μ•ˆμ „ν•˜κ²Œ μ€λ‹‰ν•˜κ³  νŠΉμ • ν•¨μˆ˜μ—κ²Œλ§Œ μƒνƒœ 변경을 ν—ˆμš©ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•œλ‹€.

 

특히 μ™ΈλΆ€ μƒνƒœ λ³€κ²½μ΄λ‚˜ λΆˆλ³€μ„±μ„ 지ν–₯ν•˜λŠ” ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œ λΆ€μˆ˜ 효과λ₯Ό μ΅œλŒ€ν•œ μ–΅μ œν•΄μ„œ 였λ₯˜λ₯Ό ν”Όν•˜κ³  ν”„λ‘œκ·Έλž¨μ˜ μ•ˆμ •μ„±μ„ 높이기 μœ„ν•΄ ν΄λ‘œμ €λŠ” 적극적으둜 μ‚¬μš©λœλ‹€. 

 

// ν•¨μˆ˜λ₯Ό 인수둜 전달받고 ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•˜λŠ” κ³ μ°¨ ν•¨μˆ˜
// 이 ν•¨μˆ˜λŠ” 카운트 μƒνƒœλ₯Ό μœ μ§€ν•˜κΈ° μœ„ν•œ 자유 λ³€μˆ˜ counterλ₯Ό κΈ°μ–΅ν•˜λŠ” ν΄λ‘œμ €λ₯Ό λ°˜ν™˜ν•œλ‹€.
function makeCounter(aux) {
  // 카운트 μƒνƒœλ₯Ό μœ μ§€ν•˜κΈ° μœ„ν•œ 자유 λ³€μˆ˜
  let counter = 0;

  // ν΄λ‘œμ €λ₯Ό λ°˜ν™˜
  return function () {
    // 인수둜 전달 받은 보쑰 ν•¨μˆ˜μ— μƒνƒœ 변경을 μœ„μž„ν•œλ‹€.
    counter = aux(counter);
    return counter;
  };
}

// 보쑰 ν•¨μˆ˜
function increase(n) {
  return ++n;
}

// 보쑰 ν•¨μˆ˜
function decrease(n) {
  return --n;
}

// ν•¨μˆ˜λ‘œ ν•¨μˆ˜λ₯Ό μƒμ„±ν•œλ‹€.
// makeCounter ν•¨μˆ˜λŠ” 보쑰 ν•¨μˆ˜λ₯Ό 인수둜 전달받아 ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•œλ‹€
const increaser = makeCounter(increase); // β‘ 
console.log(increaser()); // 1
console.log(increaser()); // 2

// increaser ν•¨μˆ˜μ™€λŠ” λ³„κ°œμ˜ λ…λ¦½λœ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ κ°–κΈ° λ•Œλ¬Έμ— μΉ΄μš΄ν„° μƒνƒœκ°€ μ—°λ™ν•˜μ§€ μ•ŠλŠ”λ‹€.
const decreaser = makeCounter(decrease); // β‘‘
console.log(decreaser()); // -1
console.log(decreaser()); // -2

πŸ‘‰ μ—¬κΈ°μ„œ makeCounter ν•¨μˆ˜κ°€ λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λŠ” μžμ‹ μ΄ μƒμ„±λ˜μ—ˆμ„ λ•Œμ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μΈ makeCounterν•¨μˆ˜μ˜ μŠ€μ½”ν”„μ— μ†ν•œ counter λ³€μˆ˜λ₯Ό κΈ°μ–΅ν•˜λŠ” ν΄λ‘œμ €μ΄λ‹€. 

그런데 이 ν•¨μˆ˜λŠ” μžμ‹ λ§Œμ˜ λ…λ¦½λœ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ κ°€μ Έμ„œ, ν˜ΈμΆœν• λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ makeCounter ν•¨μˆ˜ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈμ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ΄ μƒμ„±λœλ‹€. 

 

// ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•˜λŠ” κ³ μ°¨ ν•¨μˆ˜
// 이 ν•¨μˆ˜λŠ” 카운트 μƒνƒœλ₯Ό μœ μ§€ν•˜κΈ° μœ„ν•œ 자유 λ³€μˆ˜ counterλ₯Ό κΈ°μ–΅ν•˜λŠ” ν΄λ‘œμ €λ₯Ό λ°˜ν™˜ν•œλ‹€.
const counter = (function () {
  // 카운트 μƒνƒœλ₯Ό μœ μ§€ν•˜κΈ° μœ„ν•œ 자유 λ³€μˆ˜
  let counter = 0;

  // ν•¨μˆ˜λ₯Ό 인수둜 μ „λ‹¬λ°›λŠ” ν΄λ‘œμ €λ₯Ό λ°˜ν™˜
  return function (aux) {
    // 인수둜 전달 받은 보쑰 ν•¨μˆ˜μ— μƒνƒœ 변경을 μœ„μž„ν•œλ‹€.
    counter = aux(counter);
    return counter;
  };
}());

// 보쑰 ν•¨μˆ˜
function increase(n) {
  return ++n;
}

// 보쑰 ν•¨μˆ˜
function decrease(n) {
  return --n;
}

// 보쑰 ν•¨μˆ˜λ₯Ό μ „λ‹¬ν•˜μ—¬ 호좜
console.log(counter(increase)); // 1
console.log(counter(increase)); // 2

// 자유 λ³€μˆ˜λ₯Ό κ³΅μœ ν•œλ‹€.
console.log(counter(decrease)); // 1
console.log(counter(decrease)); // 0

πŸ‘‰ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ κ³΅μœ ν•˜λŠ” ν΄λ‘œμ €μ΄λ‹€. 

 

 


βœ”οΈ μΊ‘μŠν™”μ™€ 정보 은닉

μΊ‘μŠν™”λŠ” 객체의 ν”„λ‘œνΌν‹°μ™€ λ©”μ„œλ“œλ₯Ό ν•˜λ‚˜λ‘œ λ¬ΆλŠ” 것을 λ§ν•œλ‹€.

μΊ‘μŠν™”λŠ” 객체의 νŠΉμ • ν”„λ‘œνΌν‹°λ‚˜ λ©”μ„œλ“œλ₯Ό 감좜 λͺ©μ μœΌλ‘œ μ‚¬μš©ν•˜κΈ°λ„ ν•˜λŠ”λ° 이λ₯Ό 정보 은닉이라고 ν•œλ‹€.

 

 

 

 

πŸ‘©‍πŸ’» λͺ¨λ˜ μžλ°”μŠ€ν¬λ¦½νŠΈ Deep Diveλ₯Ό μ •λ…ν•˜κ³  μ œκ°€ μ΄ν•΄ν•œ λ‚΄μš©μ„ μ •λ¦¬ν•œ κΈ€μž…λ‹ˆλ‹€ πŸ‘©‍πŸ’»