7 minute read

24. ํด๋กœ์ € Closure

Ref: Modern JS Deep Dive: 24์žฅ ํด๋กœ์ €
  • ํด๋กœ์ €closure๋Š” ๋‚œํ•ดํ•˜๊ธฐ๋กœ ์œ ๋ช…ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜๋กœ, ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์— ๋Œ€ํ•œ ์‚ฌ์ „ ์ง€์‹์ด ์žˆ์œผ๋ฉด ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฐœ๋…์€ ์•„๋‹ˆ๋‹ค.
  • ํด๋กœ์ €๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ณ ์œ ์˜ ๊ฐœ๋…์ด ์•„๋‹ˆ๋‹ค. ํ•จ์ˆ˜๋ฅผ ์ผ๊ธ‰ ๊ฐ์ฒด๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด(์˜ˆ: ํ•˜์Šค์ผˆ, ๋ฆฌ์Šคํ”„, ์–ผ๋žญ, ์Šค์นผ๋ผ ๋“ฑ)์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ค‘์š”ํ•œ ํŠน์„ฑ์ด๋‹ค. ๊ณ ์œ ์˜ ๊ฐœ๋…์ด ์•„๋‹ˆ๋ฏ€๋กœ ECMAScript ์‚ฌ์–‘์— ๋“ฑ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • MDN์—์„œ โ€œํด๋กœ์ €๋Š” ํ•จ์ˆ˜์™€ ๊ทธ ํ•จ์ˆ˜๊ฐ€ ์„ ์–ธ๋œ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ๊ณผ์˜ ์กฐํ•ฉ์ด๋‹ค.โ€๋ผ๊ณ  ์ •์˜ํ•˜๊ณ  ์žˆ๋‹ค.

    24.1 ๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„

  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์€ ํ•จ์ˆ˜๋ฅผ ์–ด๋””์„œ ํ˜ธ์ถœํ–ˆ๋Š”์ง€๊ฐ€ ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๋ฅผ ์–ด๋””์— ์ •์˜ํ–ˆ๋Š”์ง€์— ๋”ฐ๋ผ ์ƒ์œ„ ์Šค์ฝ”ํ”„๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค. ์ด๋ฅผ ๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„(์ •์  ์Šค์ฝ”ํ”„)๋ผ ํ•œ๋‹ค.
  • ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์˜ โ€œ์™ธ๋ถ€ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์— ๋Œ€ํ•œ ์ฐธ์กฐโ€์— ์ €์žฅํ•  ์ฐธ์กฐ๊ฐ’, ์ฆ‰ ์ƒ์œ„ ์Šค์ฝ”ํ”„์— ๋Œ€ํ•œ ์ฐธ์กฐ๋Š” ํ•จ์ˆ˜ ์ •์˜๊ฐ€ ํ‰๊ฐ€๋˜๋Š” ์‹œ์ ์— ํ•จ์ˆ˜๊ฐ€ ์ •์˜๋œ ํ™˜๊ฒฝ(์œ„์น˜)์— ์˜ํ•ด ๊ฒฐ์ •๋œ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ ๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„๋‹ค.

    24.2 ํ•จ์ˆ˜ ๊ฐ์ฒด์˜ ๋‚ด๋ถ€ ์Šฌ๋กฏ [[Environment]]

  • ํ•จ์ˆ˜๋Š” ์ž์‹ ์ด ์ •์˜๋œ ํ™˜๊ฒฝ, ์ฆ‰ ์ƒ์œ„ ์Šค์ฝ”ํ”„๋ฅผ ๊ธฐ์–ตํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ํ•จ์ˆ˜๋Š” ์ž์‹ ์˜ ๋‚ด๋ถ€ ์Šฌ๋กฏ [[Environment]]์— ์ž์‹ ์ด ์ •์˜๋œ ํ™˜๊ฒฝ, ์ฆ‰ ์ƒ์œ„ ์Šค์ฝ”ํ”„์˜ ์ฐธ์กฐ๋ฅผ ์ €์žฅํ•œ๋‹ค.
  • ํ•จ์ˆ˜ ๊ฐ์ฒด์˜ ๋‚ด๋ถ€ ์Šฌ๋กฏ [[Environment]]์— ์ €์žฅ๋œ ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์˜ ์ฐธ์กฐ๊ฐ€ ๋ฐ”๋กœ ์ƒ์œ„ ์Šค์ฝ”ํ”„์ด๋‹ค. ๋˜ํ•œ ์ž์‹ ์ด ํ˜ธ์ถœ๋˜์—ˆ์„ ๋•Œ ์ƒ์„ฑ๋  ํ•จ์ˆ˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์˜ โ€œ์™ธ๋ถ€ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์— ๋Œ€ํ•œ ์ฐธ์กฐโ€์— ์ €์žฅ๋  ์ฐธ์กฐ๊ฐ’์ด๋‹ค. ํ•จ์ˆ˜ ๊ฐ์ฒด๋Š” ๋‚ด๋ถ€ ์Šฌ๋กฏ [[Environment]]์— ์ €์žฅํ•œ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์˜ ์ฐธ์กฐ, ์ฆ‰ ์ƒ์œ„ ์Šค์ฝ”ํ”„๋ฅผ ์ž์‹ ์ด ์กด์žฌํ•˜๋Š” ํ•œ ๊ธฐ์–ตํ•œ๋‹ค.

    24.3 ํด๋กœ์ €์™€ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ

  • ๋‹ค์Œ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด์ž
const x = 1;

// <1>
function outer() {
  const x = 10;
  const y = 5;
  const inner = function() { console.log(x); }; // <2>
  return inner;
}

const innerFunc = outer(); // <3>
innerFunc(); // <4> 10
  • outer ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ<3>ํ•˜๋ฉด outer ํ•จ์ˆ˜๋Š” ์ค‘์ฒฉ ํ•จ์ˆ˜ inner๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์ƒ๋ช… ์ฃผ๊ธฐlife cycle๋ฅผ ๋งˆ๊ฐํ•œ๋‹ค. ์ฆ‰, outer ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ์ด ์ข…๋ฃŒํ•˜๋ฉด outer ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๋Š” ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ์Šคํƒ์—์„œ ์ œ๊ฑฐpop๋œ๋‹ค. ์ด๋•Œ outer ํ•จ์ˆ˜์˜ ์ง€์—ญ ๋ณ€์ˆ˜ x์™€ ๋ณ€์ˆ˜ ๊ฐ’ 10์„ ์ €์žฅํ•˜๊ณ  ์žˆ๋˜ outer ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ œ๊ฑฐ๋˜์—ˆ์œผ๋ฏ€๋กœ outer ํ•จ์ˆ˜์˜ ์ง€์—ญ ๋ณ€์ˆ˜ x ๋˜ํ•œ ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ๋งˆ๊ฐํ•œ๋‹ค. ๋”ฐ๋ผ์„œ outer ํ•จ์ˆ˜์˜ ์ง€์—ญ ๋ณ€์ˆ˜ x๋Š” ๋”๋Š” ์œ ํšจํ•˜์ง€ ์•Š๊ฒŒ ๋˜์–ด x ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ฌ๋ฆฌ ์—†์–ด ๋ณด์ธ๋‹ค.
  • ๊ทธ๋Ÿฌ๋‚˜ ์œ„ ์ฝ”๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ<4>๋Š” outer ํ•จ์ˆ˜์˜ ์ง€์—ญ ๋ณ€์ˆ˜ x์˜ ๊ฐ’์ธ 10์ด๋‹ค. ์ด๋ฏธ ์ƒ๋ช… ์ฃผ๊ธฐ๊ฐ€ ์ข…๋ฃŒ๋˜์–ด ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ์Šคํƒ์—์„œ ์ œ๊ฑฐ๋œ outerํ•จ์ˆ˜์˜ ์ง€์—ญ ๋ณ€์ˆ˜ x๊ฐ€ ๋‹ค์‹œ ๋ถ€ํ™œํ•œ ๋“ฏ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋‹ค.
  • ์ด์ฒ˜๋Ÿผ ์™ธ๋ถ€ ํ•จ์ˆ˜๋ณด๋‹ค ์ค‘์ฒฉ ํ•จ์ˆ˜๊ฐ€ ๋” ์˜ค๋ž˜ ์œ ์ง€๋˜๋Š” ๊ฒฝ์šฐ ์ค‘์ฒฉ ํ•จ์ˆ˜๋Š” ์ด๋ฏธ ์ƒ๋ช… ์ฃผ๊ธฐ๊ฐ€ ์ข…๋ฃŒํ•œ ์™ธ๋ถ€ ํ•จ์ˆ˜์˜ ๋ณ€์ˆ˜๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ค‘์ฒฉ ํ•จ์ˆ˜๋ฅผ ํด๋กœ์ €closure๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.
  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๋ชจ๋“  ํ•จ์ˆ˜๋Š” ์ž์‹ ์˜ ์ƒ์œ„ ์Šค์ฝ”ํ”„๋ฅผ ๊ธฐ์–ตํ•œ๋‹ค๊ณ  ํ–ˆ๋‹ค. ํ•จ์ˆ˜๋ฅผ ์–ด๋””์„œ ํ˜ธ์ถœํ•˜๋“  ์ƒ๊ด€์—†์ด ํ•จ์ˆ˜๋Š” ์–ธ์ œ๋‚˜ ์ž์‹ ์ด ๊ธฐ์–ตํ•˜๋Š” ์ƒ์œ„ ์Šค์ฝ”ํ”„์˜ ์‹๋ณ„์ž๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์‹๋ณ„์ž์— ๋ฐ”์ธ๋”ฉ๋œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
  • ์œ„ ์˜ˆ์ œ์—์„œ ์ค‘์ฒฉ ํ•จ์ˆ˜ inner๋Š” ์ƒ์œ„ ์Šค์ฝ”ํ”„์˜ x, y ์‹๋ณ„์ž ์ค‘์— x๋งŒ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ ๋Œ€๋ถ€๋ถ„์˜ ๋ชจ๋˜ ๋ธŒ๋ผ์šฐ์ €๋Š” ์ตœ์ ํ™”๋ฅผ ํ†ตํ•ด ์ƒ์œ„ ์Šค์ฝ”ํ”„์˜ ์‹๋ณ„์ž ์ค‘์—์„œ ํด๋กœ์ €๊ฐ€ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋Š” ์‹๋ณ„์ž๋งŒ์„ ๊ธฐ์–ตํ•œ๋‹ค.
  • ํด๋กœ์ €์— ์˜ํ•ด ์ฐธ์กฐ๋˜๋Š” ์ƒ์œ„ ์Šค์ฝ”ํ”„์˜ ๋ณ€์ˆ˜๋ฅผ ์ž์œ  ๋ณ€์ˆ˜free variable๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. ํด๋กœ์ €closure๋ž€ โ€œํ•จ์ˆ˜๊ฐ€ ์ž์œ  ๋ณ€์ˆ˜์— ๋Œ€ํ•ด ๋‹ซํ˜€์žˆ๋‹คclosedโ€œ๋ผ๋Š” ์˜๋ฏธ๋‹ค. ์ด๋ฅผ ์ข€ ๋” ์•Œ๊ธฐ ์‰ฝ๊ฒŒ ์˜์—ญํ•˜์ž๋ฉด โ€œ์ž์œ  ๋ณ€์ˆ˜์— ๋ฌถ์—ฌ์žˆ๋Š” ํ•จ์ˆ˜โ€๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

24.4 ํด๋กœ์ €์˜ ํ™œ์šฉ

  • ํด๋กœ์ €๋Š” ์ƒํƒœstate๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•˜๊ณ  ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค. ๋‹ค์‹œ ๋งํ•ด, ์ƒํƒœ๊ฐ€ ์˜๋„์น˜ ์•Š๊ฒŒ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋„๋ก ์ƒํƒœ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์€๋‹‰information hidingํ•˜๊ณ  ํŠน์ • ํ•จ์ˆ˜์—๊ฒŒ๋งŒ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ํ—ˆ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
// ์นด์šดํŠธ ์ƒํƒœ ๋ณ€๊ฒฝ ํ•จ์ˆ˜
const increase = (function() {
  // ์นด์šดํŠธ ์ƒํƒœ ๋ณ€์ˆ˜
  let num = 0;
  // ํด๋กœ์ €
  return function() {
    // ์นด์šดํŠธ ์ƒํƒœ๋ฅผ 1๋งŒํผ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.
    return ++num;
  }
})());

console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
  • ์œ„ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋ฉด ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ํ•จ์ˆ˜๊ฐ€ increase ๋ณ€์ˆ˜์— ํ• ๋‹น๋œ๋‹ค. increase ๋ณ€์ˆ˜์— ํ• ๋‹น๋œ ํ•จ์ˆ˜๋Š” ์ž์‹ ์ด ์ •์˜๋œ ์œ„์น˜์— ์˜ํ•ด ๊ฒฐ์ •๋œ ์ƒ์œ„ ์Šค์ฝ”ํ”„์ธ ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €๋‹ค.
  • ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜๋Š” ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ increase๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค num ๋ณ€์ˆ˜๊ฐ€ ์žฌ์ฐจ ์ดˆ๊ธฐํ™”๋  ์ผ์€ ์—†์„ ๊ฒƒ์ด๋‹ค. ๋˜ํ•œ num ๋ณ€์ˆ˜๋Š” ์™ธ๋ถ€์—์„œ ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” ์€๋‹‰๋œ private ๋ณ€์ˆ˜์ด๋ฏ€๋กœ ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋–„์™€ ๊ฐ™์ด ์˜๋„๋˜์ง€ ์•Š์€ ๋ณ€๊ฒฝ์„ ๊ฑฑ์ •ํ•  ํ•„์š”๋„ ์—†๊ธฐ ๋–„๋ฌธ์— ๋” ์•ˆ์ •์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์ด์ฒ˜๋Ÿผ ํด๋กœ์ €๋Š” ์ƒํƒœstate๊ฐ€ ์˜๋„์น˜ ์•Š๊ฒŒ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋„๋ก ์•ˆ์ „ํ•˜๊ฒŒ ์€๋‹‰information hidingํ•˜๊ณ  ํŠน์ • ํ•จ์ˆ˜์—๊ฒŒ๋งŒ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ํ—ˆ์šฉํ•˜์—ฌ ์ƒํƒœ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•˜๊ณ  ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
const counter = (function () {
  let num = 0;
  return {
    increase() {
      return ++num;
    },
    decrease() {
      return num > 0 ? --num : 0;
    }
  };
}());
console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0
  • ์œ„ ์˜ˆ์ œ์—์„œ ์ฆ‰์‹œ ์‹คํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์€ ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜์˜ ์‹คํ–‰ ๋‹จ๊ณ„์—์„œ ํ‰๊ฐ€๋˜์–ด ๊ฐ์ฒด๊ฐ€ ๋œ๋‹ค. ์ด๋–„ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋„ ํ•จ์ˆ˜ ๊ฐ์ฒด๋กœ ์ƒ์„ฑ๋œ๋‹ค. ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์˜ ์ค‘๊ด„ํ˜ธ๋Š” ์ฝ”๋“œ ๋ธ”๋ก์ด ์•„๋‹ˆ๋ฏ€๋กœ ๋ณ„๋„์˜ ์Šค์ฝ”ํ”„๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์œ„ ์˜ˆ์ œ์˜ increase, decrease ๋ฉ”์„œ๋“œ์˜ ์ƒ์œ„ ์Šค์ฝ”ํ”„๋Š” ๋‘ ๋ฉ”์„œ๋“œ๊ฐ€ ํ‰๊ฐ€๋˜๋Š” ์‹œ์ ์— ์‹คํ–‰ ์ค‘์ธ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์ธ ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋‘ ๋ฉ”์„œ๋“œ๊ฐ€ ์–ธ์ œ ์–ด๋””์„œ ํ˜ธ์ถœ๋˜๋“  ์ƒ๊ด€์—†์ด ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜์˜ ์Šค์ฝ”ํ”„์˜ ์‹๋ณ„์ž๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์œ„ ์˜ˆ์ œ๋ฅผ ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ ํ‘œํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
const Counter = (function(){
  let num = 0;
  function Counter() {
    // this.num = 0; // ํ”„๋กœํผํ‹ฐ๋Š” publicํ•˜๋ฏ€๋กœ ์€๋‹‰๋˜์ง€ ์•Š๋Š”๋‹ค.
  }
  
  Counter.prototype.increase = function() {
    return ++num;
  };
  
  Counter.prototype.decrease = function() {
    return num > 0 ? --num : 0;
  };
  
  return Counter
})();

const counter = new Counter();
console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0
  • ๋งŒ์•ฝ num์ด ์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter๊ฐ€ ์ƒ์„ฑํ•  ์ธ์Šคํ„ด์Šค์˜ ํ”„๋กœํผํ‹ฐ๋ผ๋ฉด ์ธ์Šคํ„ด์Šค๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ์ด ์ž์œ ๋กœ์šด public ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜ ๋‚ด์—์„œ ์„ ์–ธ๋œ num ๋ณ€์ˆ˜๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜ ์™ธ๋ถ€์—์„œ๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” ์€๋‹‰๋œ ๋ณ€์ˆ˜๋‹ค.
  • ๋‹ค์Œ์€ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ํด๋กœ์ €๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋‹ค.
// ํ•จ์ˆ˜๋ฅผ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋ฐ›๊ณ  ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ณ ์ฐจ ํ•จ์ˆ˜
// ์ด ํ•จ์ˆ˜๋Š” ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ž์œ  ๋ณ€์ˆ˜ counter๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
function makeCounter(predicate) {
  // ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ž์œ  ๋ณ€์ˆ˜
  let counter = 0;
  
  // ํด๋กœ์ €๋ฅผ ๋ฐ˜ํ™˜
  return function () {
    // ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋ฐ›์€ ๋ณด์กฐ ํ•จ์ˆ˜์— ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์œ„์ž„ํ•˜๋‚Ÿ.
    counter = predicate(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

const decreaser = makeCounter(decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2

  • makerCounter ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋Š” ์ž์‹ ์ด ์ƒ์„ฑ๋์„ ๋–„์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์ธ makeCounter ํ•จ์ˆ˜์˜ ์Šค์ฝ”ํ”„์— ์†ํ•œ counter ๋ณ€์ˆ˜๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €๋‹ค.
  • ์ฃผ์˜ํ•ด์•ผ ํ•  ๊ฒƒ์€ makeCounter ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ ๋ฐ˜ํ™˜๋œ ํ•จ์ˆ˜๋Š” ์ž์‹ ๋งŒ์˜ ๋…๋ฆฝ๋œ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ๊ฐ–๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ทธ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด makeCounter ํ•จ์ˆ˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์— ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • ๋…๋ฆฝ๋œ ์นด์šดํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ ์—ฐ๋™ํ•˜์—ฌ ์ฆ๊ฐ์ด ๊ฐ€๋Šฅํ•œ ์นด์šดํ„ฐ๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ๊ณต์œ ํ•˜๋Š” ํด๋กœ์ €๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” makeCounter ํ•จ์ˆ˜๋ฅผ ๋‘ ๋ฒˆ ํ˜ธ์ถœํ•˜์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค.
// ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ณ ์ฐจ ํ•จ์ˆ˜
// ์ด ํ•จ์ˆ˜๋Š” ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ž์œ  ๋ณ€์ˆ˜ counter๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
const counter = (function () {
  // ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ž์œ  ๋ณ€์ˆ˜
  let counter = 0;
  
  // ํด๋กœ์ €๋ฅผ ๋ฐ˜ํ™˜
  return function (predicate) {
    // ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋ฐ›์€ ๋ณด์กฐ ํ•จ์ˆ˜์— ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์œ„์ž„ํ•˜๋‚Ÿ.
    counter = predicate(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

24.5 ์บก์Šํ™”์™€ ์ •๋ณด ์€๋‹‰

  • ์บก์Šํ™”encapsulation๋Š” ๊ฐ์ฒด์˜ ์ƒํƒœstate๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ”„๋กœํผํ‹ฐ์™€ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ๋™์ž‘behavior์ธ ๋ฉ”์„œ๋“œ๋ฅผ ํ•˜๋‚˜๋กœ ๋ฌถ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. ์บก์Šํ™”๋Š” ๊ฐ์ฒด์˜ ํŠน์ • ํ”„๋กœํผํ‹ฐ๋‚˜ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ์ถœ ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ๋„ ํ•˜๋Š”๋ฐ ์ด๋ฅผ ์ •๋ณด ์€๋‹‰information hiding์ด๋ผ ํ•œ๋‹ค.
  • ์ •๋ณด ์€๋‹‰์€ ์™ธ๋ถ€์— ๊ณต๊ฐœํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๊ตฌํ˜„์˜ ์ผ๋ถ€๋ฅผ ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š๋„๋ก ๊ฐ์ถ”์–ด ์ ์ ˆ์น˜ ๋ชปํ•œ ์ ‘๊ทผ์œผ๋กœ๋ถ€ํ„ฐ ๊ฐ์ฒด์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ด ์ •๋ณด๋ฅผ ๋ณดํ˜ธํ•˜๊ณ , ๊ฐ์ฒด ๊ฐ„์˜ ์ƒํ˜ธ ์˜์กด์„ฑ, ์ฆ‰ ๊ฒฐํ•ฉ๋„coupling๋ฅผ ๋‚ฎ์ถ”๋Š” ํšจ๊ณผ๊ฐ€ ์žˆ๋‹ค.
  • ๋Œ€๋ถ€๋ถ„์˜ ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋Š” ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๊ณ  ๊ทธ ํด๋ž˜์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฉค๋ฒ„(ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์„œ๋“œ)์— ๋Œ€ํ•˜์—ฌ public, private, protected ๊ฐ™์€ ์ ‘๊ทผ ์ œํ•œ์žaccess modifier๋ฅผ ์„ ์–ธํ•˜์—ฌ ๊ณต๊ฐœ ๋ฒ”์œ„๋ฅผ ํ•œ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” public, private, protected ๊ฐ™์€ ์ ‘๊ทผ ์ œํ•œ์ž๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด์˜ ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์„œ๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์–ด ์žˆ๋‹ค. ์ฆ‰, ๊ฐ์ฒด์˜ ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์„œ๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ publicํ•˜๋‹ค.
  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์ •๋ณด ์€๋‹‰์„ ์™„์ „ํ•˜๊ฒŒ ์ง€์›ํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ์ƒˆ๋กœ์šด ํ‘œ์ค€ ์‚ฌ์–‘์ด ๋‚˜์™”๋‹ค. Private class fields
class Person {
  // private ํ•„๋“œ ์ •์˜
  #name = '';
  
  constructor(name) {
    // private ํ•„๋“œ ์ฐธ์กฐ
    this.#name = name;
  }
}

const me = new Person('Lee');

// private ํ•„๋“œ #name์€ ํด๋ž˜์Šค ์™ธ๋ถ€์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์—†๋‹ค.
console.log(me.#name);
// SyntaxError: Private field '#name' must be declared in an enclosing class
  • ์ด์ฒ˜๋Ÿผ ํด๋ž˜์Šค ์™ธ๋ถ€์—์„œ private ํ•„๋“œ์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์—†๋‹ค. ๋‹ค๋งŒ ์ ‘๊ทผ์ž ํ”„๋กœํผํ‹ฐ๋ฅผ ํ†ตํ•ด ๊ฐ„์ ‘์ ์œผ๋กœ ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์œ ํšจํ•˜๋‹ค.
class Person {
  #name = '';
  
  constructor(name) {
    this.#name = name;
  }
  
  // name์€ ์ ‘๊ทผ์ž ํ”„๋กœํผํ‹ฐ๋‹ค.
  get name() {
    // private ํ•„๋“œ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ trimํ•œ ๋‹ค์Œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    return this.#name.trim();
  }
}

const me = new Person('  Lee   ');
cons
ole.log(me.name); // Lee
  • private ํ•„๋“œ๋Š” ๋ฐ˜๋“œ์‹œ ํด๋ž˜์Šค ๋ชธ์ฒด์— ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค. private ํ•„๋“œ๋ฅผ ์ง์ ‘ constructor์— ์ •์˜ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
class Person {
  constructor(name) {
    // private ํ•„๋“œ๋Š” ํด๋ž˜์Šค ๋ชธ์ฒด์—์„œ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.
    this.#name = name;
    // SyntaxError: Private field '#name' must be declared in an enclosing class
  }
}

24.6 ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ์‹ค์ˆ˜

  • ์•„๋ž˜๋Š” ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ž์ฃผ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค์ˆ˜๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์˜ˆ์ œ๋‹ค.
var funcs = [];

for (var i = 0; i < 3; i++) {
    funcs[i] = function () { return i; };
}

for (var j = 0; j < funcs.length; j++) {
    console.log(funcs[j]());
}

// 3
// 3
// 3
  • var ํ‚ค์›Œ๋“œ๋กœ ์„ ์–ธํ•œ i ๋ณ€์ˆ˜๋Š” ๋ธ”๋ก ๋ ˆ๋ฒจ ์Šค์ฝ”ํ”„๊ฐ€ ์•„๋‹Œ ํ•จ์ˆ˜ ๋ ˆ๋ฒจ ์Šค์ฝ”ํ”„๋ฅผ ๊ฐ–๊ธฐ ๋•Œ๋ฌธ์— ์ „์—ญ ๋ณ€์ˆ˜๋‹ค. ๋”ฐ๋ผ์„œ funcs ๋ฐฐ์—ด์˜ ์š”์†Œ๋กœ ์ถ”๊ฐ€ํ•œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ „์—ญ ๋ณ€์ˆ˜ i๋ฅผ ์ฐธ์กฐํ•˜์—ฌ i์˜ ๊ฐ’ 3์ด ์ถœ๋ ฅ๋œ๋‹ค.

  • ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•ด ์œ„ ์˜ˆ์ œ๋ฅผ ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋กœ ๋งŒ๋“ค์–ด ๋ณด์ž.

var funcs = [];

for (var i = 0; i < 3; i++) {
    funcs[i] = (function (id) {
        return function() {
            return id;
        };
    }(i));
}

for (var j = 0; j < funcs.length; j++) {
    console.log(funcs[j]());
}
// 0
// 1
// 2
  • ES6์˜ let ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ๊น”๋”ํ•˜๊ฒŒ ํ•ด๊ฒฐ๋œ๋‹ค.
var funcs = [];

for (let i = 0; i < 3; i++) {
    funcs[i] = function () { return i; };
}

for (let j = 0; j < funcs.length; j++) {
    console.log(funcs[j]());
}