[javascript] closure()
in JavaScript
ํด๋ก์ (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์ ์ ๊ทผํ ์ ์๋ ๊ฒ, ๋ค์ ๋งํด ์์ ์ค์ฝํ์ ์ ๊ทผํ ์ ์๋ ๊ฒ์ ๋ ์์ปฌ ์ค์ฝํ์ ๋ ํผ๋ฐ์ค๋ฅผ ์ฐจ๋ก๋๋ก ์ ์ฅํ๊ณ ์๋ ์คํ ์ปจํ ์คํธ์ ์ค์ฝํ ์ฒด์ธ์ ์๋ฐ์คํฌ๋ฆฝํธ ์์ง์ด ๊ฒ์ํ์๊ธฐ์ ๊ฐ๋ฅํ ๊ฒ์ด๋ค. ์ข๋ ์์ธํ ์ค๋ช ํ๋ฉด ์๋์ ๊ฐ๋ค.
- innerFunc ํจ์ ์ค์ฝํ(ํจ์ ์์ ์ ์ค์ฝํ๋ฅผ ๊ฐ๋ฆฌํค๋ ํ์ฑ ๊ฐ์ฒด) ๋ด์์ ๋ณ์ x๋ฅผ ๊ฒ์ํ๋ค. ๊ฒ์์ด ์คํจํ์๋ค.
- 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/