[javascript] closure()


ํด๋กœ์ €(closure)์˜ ๊ฐœ๋…

ํด๋กœ์ €(closure)๋Š” ๋‚ด๋ถ€ํ•จ์ˆ˜๊ฐ€ ์™ธ๋ถ€ํ•จ์ˆ˜์˜ ๋งฅ๋ฝ(context)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ๊ฐ€๋ฅดํ‚จ๋‹ค. ํด๋กœ์ €๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ด์šฉํ•œ ๊ณ ๋‚œ์ด๋„์˜ ํ…Œํฌ๋‹‰์„ ๊ตฌ์‚ฌํ•˜๋Š”๋ฐ ํ•„์ˆ˜์ ์ธ ๊ฐœ๋…์œผ๋กœ ํ™œ์šฉ๋œ๋‹ค.

ํด๋กœ์ €๋Š” ํ•จ์ˆ˜์™€ ํ•จ์ˆ˜๊ฐ€ ์„ ์–ธ๋œ ์–ดํœ˜์  ํ™˜๊ฒฝ์˜ ์กฐํ•ฉ์ด๋‹ค. ํด๋กœ์ €๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ณ€์ˆ˜์˜ ์œ ํšจ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•˜๋Š”์ง€(Lexical scoping)๋ฅผ ๋จผ์ € ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

ํด๋กœ์ €๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ณ ์œ ์˜ ๊ฐœ๋…์ด ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๋ฅผ ์ผ๊ธ‰ ๊ฐ์ฒด๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ค‘์š”ํ•œ ํŠน์„ฑ์ด๋‹ค.

ํด๋กœ์ €๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ณ ์œ ์˜ ๊ฐœ๋…์ด ์•„๋‹ˆ๋ฏ€๋กœ ECMAScript ๋ช…์„ธ์— ํด๋กœ์ €์˜ ์ •์˜๊ฐ€ ๋“ฑ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ํด๋กœ์ €์— ๋Œ€ํ•ด MDN์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ •์˜ํ•˜๊ณ  ์žˆ๋‹ค.


โ€œA closure is the combination of a function and the lexical environment within which that function was declared.โ€ ํด๋กœ์ €๋Š” ํ•จ์ˆ˜์™€ ๊ทธ ํ•จ์ˆ˜๊ฐ€ ์„ ์–ธ๋์„ ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ(Lexical environment)๊ณผ์˜ ์กฐํ•ฉ์ด๋‹ค.


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

outerFunc(); // 10

ํ•จ์ˆ˜ outerFunc ๋‚ด์—์„œ ๋‚ด๋ถ€ํ•จ์ˆ˜ innerFunc๊ฐ€ ์„ ์–ธ๋˜๊ณ  ํ˜ธ์ถœ๋˜์—ˆ๋‹ค. ์ด๋•Œ ๋‚ด๋ถ€ํ•จ์ˆ˜ innerFunc๋Š” ์ž์‹ ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ์™ธ๋ถ€ํ•จ์ˆ˜ outerFunc์˜ ๋ณ€์ˆ˜ x์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ํ•จ์ˆ˜ innerFunc๊ฐ€ ํ•จ์ˆ˜ outerFunc์˜ ๋‚ด๋ถ€์— ์„ ์–ธ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


์Šค์ฝ”ํ”„๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๊ฐ€ ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๋ฅผ ์–ด๋””์— ์„ ์–ธํ•˜์˜€๋Š”์ง€์— ๋”ฐ๋ผ ๊ฒฐ์ •๋œ๋‹ค. ์ด๋ฅผ [๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ•‘(Lexical scoping)๋ผ ํ•œ๋‹ค. ์œ„ ์˜ˆ์ œ์˜ ํ•จ์ˆ˜ innerFunc๋Š” ํ•จ์ˆ˜ outerFunc์˜ ๋‚ด๋ถ€์—์„œ ์„ ์–ธ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•จ์ˆ˜ innerFunc์˜ ์ƒ์œ„ ์Šค์ฝ”ํ”„๋Š” ํ•จ์ˆ˜ outerFunc์ด๋‹ค. ํ•จ์ˆ˜ innerFunc๊ฐ€ ์ „์—ญ์— ์„ ์–ธ๋˜์—ˆ๋‹ค๋ฉด ํ•จ์ˆ˜ innerFunc์˜ ์ƒ์œ„ ์Šค์ฝ”ํ”„๋Š” ์ „์—ญ ์Šค์ฝ”ํ”„๊ฐ€ ๋œ๋‹ค.


ํ•จ์ˆ˜ innerFunc๊ฐ€ ํ•จ์ˆ˜ outerFunc์˜ ๋‚ด๋ถ€์— ์„ ์–ธ๋œ ๋‚ด๋ถ€ํ•จ์ˆ˜์ด๋ฏ€๋กœ ํ•จ์ˆ˜ innerFunc๋Š” ์ž์‹ ์ด ์†ํ•œ ๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„(์ „์—ญ, ํ•จ์ˆ˜ outerFunc, ์ž์‹ ์˜ ์Šค์ฝ”ํ”„)๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๊ฒƒ์„ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์˜ ๊ด€์ ์—์„œ ์„ค๋ช…ํ•ด๋ณด์ž.

๋‚ด๋ถ€ํ•จ์ˆ˜ innerFunc๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ž์‹ ์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๊ฐ€ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ์Šคํƒ์— ์Œ“์ด๊ณ  ๋ณ€์ˆ˜ ๊ฐ์ฒด(Variable Object)์™€ ์Šค์ฝ”ํ”„ ์ฒด์ธ(Scope chain) ๊ทธ๋ฆฌ๊ณ  this์— ๋ฐ”์ธ๋”ฉํ•  ๊ฐ์ฒด๊ฐ€ ๊ฒฐ์ •๋œ๋‹ค. ์ด๋•Œ ์Šค์ฝ”ํ”„ ์ฒด์ธ์€ ์ „์—ญ ์Šค์ฝ”ํ”„๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ „์—ญ ๊ฐ์ฒด์™€ ํ•จ์ˆ˜ outerFunc์˜ ์Šค์ฝ”ํ”„๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ•จ์ˆ˜ outerFunc์˜ ํ™œ์„ฑ ๊ฐ์ฒด(Activation object) ๊ทธ๋ฆฌ๊ณ  ํ•จ์ˆ˜ ์ž์‹ ์˜ ์Šค์ฝ”ํ”„๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ™œ์„ฑ ๊ฐ์ฒด๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐ”์ธ๋”ฉํ•œ๋‹ค. ์Šค์ฝ”ํ”„ ์ฒด์ธ์ด ๋ฐ”์ธ๋”ฉํ•œ ๊ฐ์ฒด๊ฐ€ ๋ฐ”๋กœ ๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„์˜ ์‹ค์ฒด์ด๋‹ค.

๋‚ด๋ถ€ํ•จ์ˆ˜ innerFunc๊ฐ€ ์ž์‹ ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ์™ธ๋ถ€ํ•จ์ˆ˜ outerFunc์˜ ๋ณ€์ˆ˜ x์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ, ๋‹ค์‹œ ๋งํ•ด ์ƒ์œ„ ์Šค์ฝ”ํ”„์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ๋ ‰์‹œ์ปฌ ์Šค์ฝ”ํ”„์˜ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์ฐจ๋ก€๋Œ€๋กœ ์ €์žฅํ•˜๊ณ  ์žˆ๋Š” ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์˜ ์Šค์ฝ”ํ”„ ์ฒด์ธ์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์ด ๊ฒ€์ƒ‰ํ•˜์˜€๊ธฐ์— ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด๋‹ค. ์ข€๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  1. innerFunc ํ•จ์ˆ˜ ์Šค์ฝ”ํ”„(ํ•จ์ˆ˜ ์ž์‹ ์˜ ์Šค์ฝ”ํ”„๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ™œ์„ฑ ๊ฐ์ฒด) ๋‚ด์—์„œ ๋ณ€์ˆ˜ x๋ฅผ ๊ฒ€์ƒ‰ํ•œ๋‹ค. ๊ฒ€์ƒ‰์ด ์‹คํŒจํ•˜์˜€๋‹ค.
  2. innerFunc ํ•จ์ˆ˜๋ฅผ ํฌํ•จํ•˜๋Š” ์™ธ๋ถ€ ํ•จ์ˆ˜ outerFunc์˜ ์Šค์ฝ”ํ”„(ํ•จ์ˆ˜ outerFunc์˜ ์Šค์ฝ”ํ”„๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ•จ์ˆ˜ outerFunc์˜ ํ™œ์„ฑ ๊ฐ์ฒด)์—์„œ ๋ณ€์ˆ˜ x๋ฅผ ๊ฒ€์ƒ‰ํ•œ๋‹ค. ๊ฒ€์ƒ‰์ด ์„ฑ๊ณตํ•˜์˜€๋‹ค.

์ด๋ฒˆ์—๋Š” ๋‚ด๋ถ€ํ•จ์ˆ˜ innerFunc๋ฅผ ํ•จ์ˆ˜ outerFunc ๋‚ด์—์„œ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋ณ€๊ฒฝํ•ด ๋ณด์ž.

function outerFunc() {
  var x = 10;
  var innerFunc = function () { console.log(x); };
  return innerFunc;
}

/**
 *  ํ•จ์ˆ˜ outerFunc๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋‚ด๋ถ€ ํ•จ์ˆ˜ innerFunc๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.
 *  ๊ทธ๋ฆฌ๊ณ  ํ•จ์ˆ˜ outerFunc์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๋Š” ์†Œ๋ฉธํ•œ๋‹ค.
 */
var inner = outerFunc();
inner(); // 10

ํ•จ์ˆ˜ outerFunc๋Š” ๋‚ด๋ถ€ํ•จ์ˆ˜ innerFunc๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์ƒ์„ ๋งˆ๊ฐํ–ˆ๋‹ค. ์ฆ‰, ํ•จ์ˆ˜ outerFunc๋Š” ์‹คํ–‰๋œ ์ดํ›„ ์ฝœ์Šคํƒ(์‹คํ–‰ ์ปจํ…์ŠคํŠธ ์Šคํƒ)์—์„œ ์ œ๊ฑฐ๋˜์—ˆ์œผ๋ฏ€๋กœ ํ•จ์ˆ˜ outerFunc์˜ ๋ณ€์ˆ˜ x ๋˜ํ•œ ๋”์ด์ƒ ์œ ํšจํ•˜์ง€ ์•Š๊ฒŒ ๋˜์–ด ๋ณ€์ˆ˜ x์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ฌ๋ฆฌ ์—†์–ด ๋ณด์ธ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์œ„ ์ฝ”๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” ๋ณ€์ˆ˜ x์˜ ๊ฐ’์ธ 10์ด๋‹ค. ์ด๋ฏธ life-cycle์ด ์ข…๋ฃŒ๋˜์–ด ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ์Šคํƒ์—์„œ ์ œ๊ฑฐ๋œ ํ•จ์ˆ˜ outerFunc์˜ ์ง€์—ญ๋ณ€์ˆ˜ x๊ฐ€ ๋‹ค์‹œ ๋ถ€ํ™œ์ด๋ผ๋„ ํ•œ ๋“ฏ์ด ๋™์ž‘ํ•˜๊ณ  ์žˆ๋‹ค. ๋ญ”๊ฐ€ ํŠน๋ณ„ํ•œ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

์ด์ฒ˜๋Ÿผ ์ž์‹ ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ์™ธ๋ถ€ํ•จ์ˆ˜๋ณด๋‹ค ๋‚ด๋ถ€ํ•จ์ˆ˜๊ฐ€ ๋” ์˜ค๋ž˜ ์œ ์ง€๋˜๋Š” ๊ฒฝ์šฐ, ์™ธ๋ถ€ ํ•จ์ˆ˜ ๋ฐ–์—์„œ ๋‚ด๋ถ€ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋”๋ผ๋„ ์™ธ๋ถ€ํ•จ์ˆ˜์˜ ์ง€์—ญ ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋Ÿฌํ•œ ํ•จ์ˆ˜๋ฅผ ํด๋กœ์ €(Closure)๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.


ํด๋กœ์ €๋Š” ํ•จ์ˆ˜์™€ ๊ทธ ํ•จ์ˆ˜๊ฐ€ ์„ ์–ธ๋์„ ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ(Lexical environment)๊ณผ์˜ ์กฐํ•ฉ์ด๋‹ค.


์œ„ ์ •์˜์—์„œ ๋งํ•˜๋Š” โ€œํ•จ์ˆ˜โ€๋ž€ ๋ฐ˜ํ™˜๋œ ๋‚ด๋ถ€ํ•จ์ˆ˜๋ฅผ ์˜๋ฏธํ•˜๊ณ  โ€œ๊ทธ ํ•จ์ˆ˜๊ฐ€ ์„ ์–ธ๋  ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ(Lexical environment)โ€๋ž€ ๋‚ด๋ถ€ ํ•จ์ˆ˜๊ฐ€ ์„ ์–ธ๋์„ ๋•Œ์˜ ์Šค์ฝ”ํ”„๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ์ฆ‰, ํด๋กœ์ €๋Š” ๋ฐ˜ํ™˜๋œ ๋‚ด๋ถ€ํ•จ์ˆ˜๊ฐ€ ์ž์‹ ์ด ์„ ์–ธ๋์„ ๋•Œ์˜ ํ™˜๊ฒฝ(Lexical environment)์ธ ์Šค์ฝ”ํ”„๋ฅผ ๊ธฐ์–ตํ•˜์—ฌ ์ž์‹ ์ด ์„ ์–ธ๋์„ ๋•Œ์˜ ํ™˜๊ฒฝ(์Šค์ฝ”ํ”„) ๋ฐ–์—์„œ ํ˜ธ์ถœ๋˜์–ด๋„ ๊ทธ ํ™˜๊ฒฝ(์Šค์ฝ”ํ”„)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ๋งํ•œ๋‹ค. ์ด๋ฅผ ์กฐ๊ธˆ ๋” ๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด ํด๋กœ์ €๋Š” ์ž์‹ ์ด ์ƒ์„ฑ๋  ๋•Œ์˜ ํ™˜๊ฒฝ(Lexical environment)์„ ๊ธฐ์–ตํ•˜๋Š” ํ•จ์ˆ˜๋‹ค๋ผ๊ณ  ๋งํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

ํด๋กœ์ €์— ์˜ํ•ด ์ฐธ์กฐ๋˜๋Š” ์™ธ๋ถ€ํ•จ์ˆ˜์˜ ๋ณ€์ˆ˜ ์ฆ‰ outerFunc ํ•จ์ˆ˜์˜ ๋ณ€์ˆ˜ x๋ฅผ ์ž์œ ๋ณ€์ˆ˜(Free variable)๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. ํด๋กœ์ €๋ผ๋Š” ์ด๋ฆ„์€ ์ž์œ ๋ณ€์ˆ˜์— ํ•จ์ˆ˜๊ฐ€ ๋‹ซํ˜€์žˆ๋‹ค(closed)๋ผ๋Š” ์˜๋ฏธ๋กœ ์˜์—ญํ•˜๋ฉด ์ž์œ ๋ณ€์ˆ˜์— ์—ฎ์—ฌ์žˆ๋Š” ํ•จ์ˆ˜๋ผ๋Š” ๋œป์ด๋‹ค.

์‹คํ–‰ ์ปจํ…์ŠคํŠธ์˜ ๊ด€์ ์— ์„ค๋ช…ํ•˜๋ฉด, ๋‚ด๋ถ€ํ•จ์ˆ˜๊ฐ€ ์œ ํšจํ•œ ์ƒํƒœ์—์„œ ์™ธ๋ถ€ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒํ•˜์—ฌ ์™ธ๋ถ€ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋ฐ˜ํ™˜๋˜์–ด๋„, ์™ธ๋ถ€ํ•จ์ˆ˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ๋‚ด์˜ ํ™œ์„ฑ ๊ฐ์ฒด(Activation object)(๋ณ€์ˆ˜, ํ•จ์ˆ˜ ์„ ์–ธ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค)๋Š” ๋‚ด๋ถ€ํ•จ์ˆ˜์— ์˜ํ•ด ์ฐธ์กฐ๋˜๋Š” ํ•œ ์œ ํšจํ•˜์—ฌ ๋‚ด๋ถ€ํ•จ์ˆ˜๊ฐ€ ์Šค์ฝ”ํ”„ ์ฒด์ธ์„ ํ†ตํ•ด ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

์ฆ‰ ์™ธ๋ถ€ํ•จ์ˆ˜๊ฐ€ ์ด๋ฏธ ๋ฐ˜ํ™˜๋˜์—ˆ์–ด๋„ ์™ธ๋ถ€ํ•จ์ˆ˜ ๋‚ด์˜ ๋ณ€์ˆ˜๋Š” ์ด๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š” ๋‚ด๋ถ€ํ•จ์ˆ˜๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ๊ณ„์† ์œ ์ง€๋œ๋‹ค. ์ด๋•Œ ๋‚ด๋ถ€ํ•จ์ˆ˜๊ฐ€ ์™ธ๋ถ€ํ•จ์ˆ˜์— ์žˆ๋Š” ๋ณ€์ˆ˜์˜ ๋ณต์‚ฌ๋ณธ์ด ์•„๋‹ˆ๋ผ ์‹ค์ œ ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•œ๋‹ค๋Š” ๊ฒƒ์— ์ฃผ์˜ํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

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

ํด๋กœ์ €๋Š” ์ž์‹ ์ด ์ƒ์„ฑ๋  ๋•Œ์˜ ํ™˜๊ฒฝ(Lexical environment)์„ ๊ธฐ์–ตํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ์ฐจ์›์—์„œ ์†ํ•ด๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ํด๋กœ์ €๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์œผ๋กœ ์ด๋ฅผ ์ ๊ทน์ ์œผ๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ํด๋กœ์ €๊ฐ€ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ์ƒํ™ฉ์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์ž.

2.1 ์ƒํƒœ์œ ์ง€

ํด๋กœ์ €๊ฐ€ ๊ฐ€์žฅ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ์ƒํ™ฉ์€ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๊ธฐ์–ตํ•˜๊ณ  ๋ณ€๊ฒฝ๋œ ์ตœ์‹  ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์•„๋ž˜ ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด์ž.

<!DOCTYPE html>
<html>
<body>
  <button class="toggle">toggle</button>
  <div class="box" style="width: 100px; height: 100px; background: red;"></div>

  <script>
    var box = document.querySelector('.box');
    var toggleBtn = document.querySelector('.toggle');

    var toggle = (function () {
      var isShow = false;

      // โ‘  ํด๋กœ์ €๋ฅผ ๋ฐ˜ํ™˜
      return function () {
        box.style.display = isShow ? 'block' : 'none';
        // โ‘ข ์ƒํƒœ ๋ณ€๊ฒฝ
        isShow = !isShow;
      };
    })();

    // โ‘ก ์ด๋ฒคํŠธ ํ”„๋กœํผํ‹ฐ์— ํด๋กœ์ €๋ฅผ ํ• ๋‹น
    toggleBtn.onclick = toggle;
  </script>
</body>
</html>

โ‘  ์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์ฆ‰์‹œ ์†Œ๋ฉธํ•œ๋‹ค. ์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ํ•จ์ˆ˜๋Š” ์ž์‹ ์ด ์ƒ์„ฑ๋์„ ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ(Lexical environment)์— ์†ํ•œ ๋ณ€์ˆ˜ isShow๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €๋‹ค. ํด๋กœ์ €๊ฐ€ ๊ธฐ์–ตํ•˜๋Š” ๋ณ€์ˆ˜ isShow๋Š” box ์š”์†Œ์˜ ํ‘œ์‹œ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

โ‘ก ํด๋กœ์ €๋ฅผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ์„œ ์ด๋ฒคํŠธ ํ”„๋กœํผํ‹ฐ์— ํ• ๋‹นํ–ˆ๋‹ค. ์ด๋ฒคํŠธ ํ”„๋กœํผํ‹ฐ์—์„œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์ธ ํด๋กœ์ €๋ฅผ ์ œ๊ฑฐํ•˜์ง€ ์•Š๋Š” ํ•œ ํด๋กœ์ €๊ฐ€ ๊ธฐ์–ตํ•˜๋Š” ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์˜ ๋ณ€์ˆ˜ isShow๋Š” ์†Œ๋ฉธํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‹ค์‹œ ๋งํ•ด ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๊ธฐ์–ตํ•œ๋‹ค.

โ‘ข ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ด๋ฒคํŠธ ํ”„๋กœํผํ‹ฐ์— ํ• ๋‹นํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์ธ ํด๋กœ์ €๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. ์ด๋•Œ .box ์š”์†Œ์˜ ํ‘œ์‹œ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ณ€์ˆ˜ isShow์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ๋‹ค. ๋ณ€์ˆ˜ isShow๋Š” ํด๋กœ์ €์— ์˜ํ•ด ์ฐธ์กฐ๋˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์œ ํšจํ•˜๋ฉฐ ์ž์‹ ์˜ ๋ณ€๊ฒฝ๋œ ์ตœ์‹  ์ƒํƒœ๋ฅผ ๊ฒŒ์†ํ•ด์„œ ์œ ์ง€ํ•œ๋‹ค.

์ด์ฒ˜๋Ÿผ ํด๋กœ์ €๋Š” ํ˜„์žฌ ์ƒํƒœ(์œ„ ์˜ˆ์ œ์˜ ๊ฒฝ์šฐ .box ์š”์†Œ์˜ ํ‘œ์‹œ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” isShow ๋ณ€์ˆ˜)๋ฅผ ๊ธฐ์–ตํ•˜๊ณ  ์ด ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด๋„ ์ตœ์‹  ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์— ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค. ๋งŒ์•ฝ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์— ํด๋กœ์ €๋ผ๋Š” ๊ธฐ๋Šฅ์ด ์—†๋‹ค๋ฉด ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค. ์ „์—ญ ๋ณ€์ˆ˜๋Š” ์–ธ์ œ๋“ ์ง€ ๋ˆ„๊ตฌ๋‚˜ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ณ  ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ๋ถ€์ž‘์šฉ์„ ์œ ๋ฐœํ•ด ์˜ค๋ฅ˜์˜ ์›์ธ์ด ๋˜๋ฏ€๋กœ ์‚ฌ์šฉ์„ ์–ต์ œํ•ด์•ผ ํ•œ๋‹ค.

2.2 ์ „์—ญ ๋ณ€์ˆ˜์˜ ์‚ฌ์šฉ ์–ต์ œ

๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ๋งˆ๋‹ค ํด๋ฆญํ•œ ํšŸ์ˆ˜๊ฐ€ ๋ˆ„์ ๋˜์–ด ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ์นด์šดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž. ์ด ์˜ˆ์ œ์˜ ํด๋ฆญ๋œ ํšŸ์ˆ˜๊ฐ€ ๋ฐ”๋กœ ์œ ์ง€ํ•ด์•ผํ•  ์ƒํƒœ์ด๋‹ค.

<!DOCTYPE html>
<html>
<body>
  <p>์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ Counting</p>
  <button id="inclease">+</button>
  <p id="count">0</p>
  <script>
    var incleaseBtn = document.getElementById('inclease');
    var count = document.getElementById('count');

    // ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ „์—ญ ๋ณ€์ˆ˜
    var counter = 0;

    function increase() {
      return ++counter;
    }

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
  </script>
</body>
</html>

์œ„ ์ฝ”๋“œ๋Š” ์ž˜ ๋™์ž‘ํ•˜์ง€๋งŒ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ๊ฐ€๋Šฅ์„ฑ์„ ๋‚ดํฌํ•˜๊ณ  ์žˆ๋Š” ์ข‹์ง€ ์•Š์€ ์ฝ”๋“œ๋‹ค. increase ํ•จ์ˆ˜๋Š” ํ˜ธ์ถœ๋˜๊ธฐ ์ง์ „์— ์ „์—ญ๋ณ€์ˆ˜ counter์˜ ๊ฐ’์ด ๋ฐ˜๋“œ์‹œ 0์ด์—ฌ์•ผ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋ณ€์ˆ˜ counter๋Š” ์ „์—ญ ๋ณ€์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ์–ธ์ œ๋“ ์ง€ ๋ˆ„๊ตฌ๋‚˜ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ณ  ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ์˜๋„์น˜ ์•Š๊ฒŒ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ๋งŒ์•ฝ ๋ˆ„๊ตฐ๊ฐ€์— ์˜ํ•ด ์˜๋„์น˜ ์•Š๊ฒŒ ์ „์—ญ ๋ณ€์ˆ˜ counter์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋๋‹ค๋ฉด ์ด๋Š” ์˜ค๋ฅ˜๋กœ ์ด์–ด์ง„๋‹ค. ๋ณ€์ˆ˜ counter๋Š” ์นด์šดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” increase ํ•จ์ˆ˜๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค. ์ „์—ญ ๋ณ€์ˆ˜ counter๋ฅผ increase ํ•จ์ˆ˜์˜ ์ง€์—ญ ๋ณ€์ˆ˜๋กœ ๋ฐ”๊พธ์–ด ์˜๋„์น˜ ์•Š์€ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ๋ฐฉ์ง€ํ•ด๋ณด์ž.

<!DOCTYPE html>
<html>
<body>
  <p>์ง€์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ Counting</p>
  <button id="inclease">+</button>
  <p id="count">0</p>
  <script>
    var incleaseBtn = document.getElementById('inclease');
    var count = document.getElementById('count');

    function increase() {
      // ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ง€์—ญ ๋ณ€์ˆ˜
      var counter = 0;
      return ++counter;
    }

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
  </script>
</body>
</html>

์ „์—ญ๋ณ€์ˆ˜๋ฅผ ์ง€์—ญ๋ณ€์ˆ˜๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์˜๋„์น˜ ์•Š์€ ์ƒํƒœ ๋ณ€๊ฒฝ์€ ๋ฐฉ์ง€ํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ increase ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ์ง€์—ญ๋ณ€์ˆ˜ counter๋ฅผ 0์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์–ธ์ œ๋‚˜ 1์ด ํ‘œ์‹œ๋œ๋‹ค. ๋‹ค์‹œ ๋งํ•ด ๋ณ€๊ฒฝ๋œ ์ด์ „ ์ƒํƒœ๋ฅผ ๊ธฐ์–ตํ•˜์ง€ ๋ชปํ•œ๋‹ค. ์ด์ „ ์ƒํƒœ๋ฅผ ๊ธฐ์–ตํ•˜๋„๋ก ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด๋ณด์ž.

<!DOCTYPE html>
<html>
  <body>
  <p>ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•œ Counting</p>
  <button id="inclease">+</button>
  <p id="count">0</p>
  <script>
    var incleaseBtn = document.getElementById('inclease');
    var count = document.getElementById('count');

    var increase = (function () {
      // ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ž์œ  ๋ณ€์ˆ˜
      var counter = 0;
      // ํด๋กœ์ €๋ฅผ ๋ฐ˜ํ™˜
      return function () {
        return ++counter;
      };
    }());

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
  </script>
</body>
</html>

์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋˜๋ฉด ์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜(immediately-invoked function expression)๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ๋ณ€์ˆ˜ increase์—๋Š” ํ•จ์ˆ˜ function () { return ++counter; }๊ฐ€ ํ• ๋‹น๋œ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ž์‹ ์ด ์ƒ์„ฑ๋์„ ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ(Lexical environment)์„ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €๋‹ค. ์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜๋Š” ํ˜ธ์ถœ๋œ ์ดํ›„ ์†Œ๋ฉธ๋˜์ง€๋งŒ ์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ํ•จ์ˆ˜๋Š” ๋ณ€์ˆ˜ increase์— ํ• ๋‹น๋˜์–ด inclease ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํด๋ฆญ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€์—์„œ ํ˜ธ์ถœ๋œ๋‹ค. ์ด๋•Œ ํด๋กœ์ €์ธ ์ด ํ•จ์ˆ˜๋Š” ์ž์‹ ์ด ์„ ์–ธ๋์„ ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์ธ ์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜์˜ ์Šค์ฝ”ํ”„์— ์†ํ•œ ์ง€์—ญ๋ณ€์ˆ˜ counter๋ฅผ ๊ธฐ์–ตํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜์˜ ๋ณ€์ˆ˜ counter์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ณ  ๋ณ€์ˆ˜ counter๋Š” ์ž์‹ ์„ ์ฐธ์กฐํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์†Œ๋ฉธ๋  ๋•Œ๊ฐ€์ง€ ์œ ์ง€๋œ๋‹ค.

์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜๋Š” ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ increase๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ๋ณ€์ˆ˜ counter๊ฐ€ ์žฌ์ฐจ ์ดˆ๊ธฐํ™”๋  ์ผ์€ ์—†์„ ๊ฒƒ์ด๋‹ค. ๋ณ€์ˆ˜ counter๋Š” ์™ธ๋ถ€์—์„œ ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” private ๋ณ€์ˆ˜์ด๋ฏ€๋กœ ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ์™€ ๊ฐ™์ด ์˜๋„๋˜์ง€ ์•Š์€ ๋ณ€๊ฒฝ์„ ๊ฑฑ์ •ํ•  ํ•„์š”๋„ ์—†๊ธฐ ๋•Œ๋ฌธ์ด ๋ณด๋‹ค ์•ˆ์ •์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.


๋ณ€์ˆ˜์˜ ๊ฐ’์€ ๋ˆ„๊ตฐ๊ฐ€์— ์˜ํ•ด ์–ธ์ œ๋“ ์ง€ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์–ด ์˜ค๋ฅ˜ ๋ฐœ์ƒ์˜ ๊ทผ๋ณธ์  ์›์ธ์ด ๋  ์ˆ˜ ์žˆ๋‹ค. ์ƒํƒœ ๋ณ€๊ฒฝ์ด๋‚˜ ๊ฐ€๋ณ€(mutable) ๋ฐ์ดํ„ฐ๋ฅผ ํ”ผํ•˜๊ณ  ๋ถˆ๋ณ€์„ฑ(Immutability)์„ ์ง€ํ–ฅํ•˜๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ๋ถ€์ˆ˜ ํšจ๊ณผ(Side effect)๋ฅผ ์ตœ๋Œ€ํ•œ ์–ต์ œํ•˜์—ฌ ์˜ค๋ฅ˜๋ฅผ ํ”ผํ•˜๊ณ  ํ”„๋กœ๊ทธ๋žจ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ํด๋กœ์ €๋Š” ์ ๊ทน์ ์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.


์•„๋ž˜๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ํด๋กœ์ €๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ์ด๋‹ค.

// ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›๊ณ  ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ณ ์ฐจ ํ•จ์ˆ˜
// ์ด ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋Š” ํด๋กœ์ €๋กœ์„œ ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ž์œ  ๋ณ€์ˆ˜ counter์„ ๊ธฐ์–ตํ•œ๋‹ค.
function makeCounter(predicate) {
  // ์นด์šดํŠธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ž์œ  ๋ณ€์ˆ˜
  var 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

// increaser ํ•จ์ˆ˜์™€๋Š” ๋ณ„๊ฐœ์˜ ๋…๋ฆฝ๋œ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ๊ฐ–๊ธฐ ๋•Œ๋ฌธ์— ์นด์šดํ„ฐ ์ƒํƒœ๊ฐ€ ์—ฐ๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค.
const decreaser = makeCounter(decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2

ํ•จ์ˆ˜ makeCounter๋Š” ๋ณด์กฐ ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›๊ณ  ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ณ ์ฐจ ํ•จ์ˆ˜์ด๋‹ค. ํ•จ์ˆ˜ makeCounter๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋Š” ์ž์‹ ์ด ์ƒ์„ฑ๋์„ ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์ธ ํ•จ์ˆ˜ makeCounter์˜ ์Šค์ฝ”ํ”„์— ์†ํ•œ ๋ณ€์ˆ˜ counter์„ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €๋‹ค. ํ•จ์ˆ˜ makeCounter๋Š” ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›์€ ๋ณด์กฐ ํ•จ์ˆ˜๋ฅผ ํ•ฉ์„ฑํ•˜์—ฌ ์ž์‹ ์ด ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์˜ ๋™์ž‘์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•  ๊ฒƒ์€ ํ•จ์ˆ˜ makeCounter๋ฅผ ํ˜ธ์ถœํ•ด ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ ๋ฐ˜ํ™˜๋œ ํ•จ์ˆ˜๋Š” ์ž์‹ ๋งŒ์˜ ๋…๋ฆฝ๋œ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ๊ฐ–๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ทธ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์ด ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์œ„ ์˜ˆ์ œ์—์„œ ๋ณ€์ˆ˜ increaser์™€ ๋ณ€์ˆ˜ decreaser์— ํ• ๋‹น๋œ ํ•จ์ˆ˜๋Š” ๊ฐ๊ฐ ์ž์‹ ๋งŒ์˜ ๋…๋ฆฝ๋œ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ๊ฐ–๊ธฐ ๋•Œ๋ฌธ์— ์นด์šดํŠธ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ž์œ  ๋ณ€์ˆ˜ counter๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š์•„ ์นด์šดํ„ฐ์˜ ์ฆ๊ฐ์ด ์—ฐ๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ๋…๋ฆฝ๋œ ์นด์šดํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ ์—ฐ๋™ํ•˜์—ฌ ์ฆ๊ฐ์ด ๊ฐ€๋Šฅํ•œ ์นด์šดํ„ฐ๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ๊ณต์œ ํ•˜๋Š” ํด๋กœ์ €๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

2.3 ์ •๋ณด์˜ ์€๋‹‰

์ด๋ฒˆ์—๋Š” ์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด๋ฅผ ํ†ตํ•ด counter ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

function Counter() {
  // ์นด์šดํŠธ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ž์œ  ๋ณ€์ˆ˜
  var counter = 0;

  // ํด๋กœ์ €
  this.increase = function () {
    return ++counter;
  };

  // ํด๋กœ์ €
  this.decrease = function () {
    return --counter;
  };
}

const counter = new Counter();

console.log(counter.increase()); // 1
console.log(counter.decrease()); // 0

์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter๋Š” increase, decrease ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ–๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋“ค์€ ๋ชจ๋‘ ์ž์‹ ์ด ์ƒ์„ฑ๋์„ ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์ธ ์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter์˜ ์Šค์ฝ”ํ”„์— ์†ํ•œ ๋ณ€์ˆ˜ counter๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ํด๋กœ์ €์ด๋ฉฐ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์„ ๊ณต์œ ํ•œ๋‹ค. ์ƒ์„ฑ์ž ํ•จ์ˆ˜๊ฐ€ ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑํ•œ ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ๋Š” ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ์—๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ฉฐ ์ž์‹ ์ด ๊ธฐ์–ตํ•˜๋Š” ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์˜ ๋ณ€์ˆ˜์—๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋•Œ ์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter์˜ ๋ณ€์ˆ˜ counter๋Š” this์— ๋ฐ”์ธ๋”ฉ๋œ ํ”„๋กœํผํ‹ฐ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ณ€์ˆ˜๋‹ค. counter๊ฐ€ this์— ๋ฐ”์ธ๋”ฉ๋œ ํ”„๋กœํผํ‹ฐ๋ผ๋ฉด ์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter๊ฐ€ ์ƒ์„ฑํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•œ public ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋˜์ง€๋งŒ ์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter ๋‚ด์—์„œ ์„ ์–ธ๋œ ๋ณ€์ˆ˜ counter๋Š” ์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter ์™ธ๋ถ€์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค. ํ•˜์ง€๋งŒ ์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter๊ฐ€ ์ƒ์„ฑํ•œ ์ธ์Šคํ„ด์Šค์˜ ๋ฉ”์†Œ๋“œ์ธ increase, decrease๋Š” ํด๋กœ์ €์ด๊ธฐ ๋•Œ๋ฌธ์— ์ž์‹ ์ด ์ƒ์„ฑ๋์„ ๋•Œ์˜ ๋ ‰์‹œ์ปฌ ํ™˜๊ฒฝ์ธ ์ƒ์„ฑ์ž ํ•จ์ˆ˜ Counter์˜ ๋ณ€์ˆ˜ counter์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ํด๋กœ์ €์˜ ํŠน์ง•์„ ์‚ฌ์šฉํ•ด ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ์–ธ์–ด์˜ private ํ‚ค์›Œ๋“œ๋ฅผ ํ‰๋‚ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

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

์•„๋ž˜์˜ ์˜ˆ์ œ๋Š” ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ž์ฃผ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค์ˆ˜์— ๊ด€๋ จํ•œ ์˜ˆ์ œ๋‹ค.

var arr = [];

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

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

๋ฐฐ์—ด arr์— 5๊ฐœ์˜ ํ•จ์ˆ˜๊ฐ€ ํ• ๋‹น๋˜๊ณ  ๊ฐ๊ฐ์˜ ํ•จ์ˆ˜๋Š” ์ˆœ์ฐจ์ ์œผ๋กœ 0, 1, 2, 3, 4๋ฅผ ๋ฐ˜ํ™˜ํ•  ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€ํ•˜๊ฒ ์ง€๋งŒ ๊ฒฐ๊ณผ๋Š” ๊ทธ๋ ‡์ง€์•Š๋‹ค. for ๋ฌธ์—์„œ ์‚ฌ์šฉํ•œ ๋ณ€์ˆ˜ i๋Š” ์ „์—ญ ๋ณ€์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋กœ ๋งŒ๋“ค์–ด๋ณด์ž.

var arr = [];

for (var i = 0; i < 5; i++){
  arr[i] = (function (id) { // โ‘ก
    return function () {
      return id; // โ‘ข
    };
  }(i)); // โ‘ 
}

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

โ‘  ๋ฐฐ์—ด arr์—๋Š” ์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜์— ์˜ํ•ด ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

โ‘ก ์ด๋•Œ ์ฆ‰์‹œ์‹คํ–‰ํ•จ์ˆ˜๋Š” i๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›๊ณ  ๋งค๊ฐœ๋ณ€์ˆ˜ id์— ํ• ๋‹นํ•œ ํ›„ ๋‚ด๋ถ€ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  life-cycle์ด ์ข…๋ฃŒ๋œ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜ id๋Š” ์ž์œ ๋ณ€์ˆ˜๊ฐ€ ๋œ๋‹ค.

โ‘ข ๋ฐฐ์—ด arr์— ํ• ๋‹น๋œ ํ•จ์ˆ˜๋Š” id๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ด๋•Œ id๋Š” ์ƒ์œ„ ์Šค์ฝ”ํ”„์˜ ์ž์œ ๋ณ€์ˆ˜์ด๋ฏ€๋กœ ๊ทธ ๊ฐ’์ด ์œ ์ง€๋œ๋‹ค.

์œ„ ์˜ˆ์ œ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ํ•จ์ˆ˜ ๋ ˆ๋ฒจ ์Šค์ฝ”ํ”„ ํŠน์„ฑ์œผ๋กœ ์ธํ•ด for ๋ฃจํ”„์˜ ์ดˆ๊ธฐ๋ฌธ์—์„œ ์‚ฌ์šฉ๋œ ๋ณ€์ˆ˜์˜ ์Šค์ฝ”ํ”„๊ฐ€ ์ „์—ญ์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ์ด๋‹ค. ES6์˜ let ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด์™€ ๊ฐ™์€ ๋ฌธ์ œ๋Š” ๋ง๋”ํžˆ ํ•ด๊ฒฐ๋œ๋‹ค.

const arr = [];

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

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

๋˜๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ฒ•์ธ ๊ณ ์ฐจ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ๋ณ€์ˆ˜์™€ ๋ฐ˜๋ณต๋ฌธ์˜ ์‚ฌ์šฉ์„ ์–ต์ œํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์—ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์˜ค๋ฅ˜๋ฅผ ์ค„์ด๊ณ  ๊ฐ€๋…์„ฑ์„ ์ข‹๊ฒŒ ๋งŒ๋“ ๋‹ค.

const arr = new Array(5).fill();

arr.forEach((v, i, array) => array[i] = () => i);

arr.forEach(f => console.log(f()));
Refrence

https://poiemaweb.com/js-closure

http://dmitrysoshnikov.com/ecmascript/chapter-6-closures/