Javascript로 개발하다보면, 처음에는 아주 쉽고 재밌게 느껴지며 너무 편하다고생각이 된다.
그러나 일정수준 이상되면 첫번째 한계에 부딪히게 되는데 그게바로 "Closure" 이다.
심지어 clousre에 대한 job interview 대답에 따라 $40k 까지 연봉이 차이가 날수도 있다는 blog posting까지 보았다.
(https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36)
(뇌피셜일수도 있으니 주의하자)
그럼 이토록 중요한 클로저가 무엇인지 다시한번 상기하고 필요성에 대해서 얘기해보고자 한다.
Closure
먼저 클로저를 얘기하기전에 var에 대해서 이야기해보자, var로 선언된 변수는 기본적으로 function scope 내에서만 사용되어진다.
function() {
var a = 1;
console.log(a); // works
}
console.log(a); // fails
내가만약 위의 함수를 오류가 없이 동작시키려면 global scope를 사용해야 할것이다.
var a = 1;
function() {
console.log(a); // works
}
console.log(a); // works
지역변수는 보통 function이 끝나게 되면 메모리에서 사라진다.
그렇다면 우리는 local variable을 계속 참조 할 수 없을까? 이를 closure가 답변해준다.
클로저는 쉽게말해 persistance한 local variable scope이다. (persistance = 영속적인 한글로 표현하면 좀 어색해진다.)
클로저는 코드의 실행이 function 밖으로 이동했음에도 불구하고 지속되는 persistance local scope이다.
더 쉽게말해서 function scope 밖에서도 그 local variable에서 변수를 reference 할 수 있게 해준다.
(객체지향적 설계를 가능하게 한다.)
다음을 코드를 보면 더 이해가 잘 될것이다.
outer = function() {
var a = 1;
var inner = function() {
console.log(a);
}
var add = function() {
a += 1;
}
return {inner,add}; // this returns a function
}
var fnc = outer(); // execute outer to get inner
fnc.inner();
fnc.add();
fnc.inner();
//output
//1
//2
위 코드에서처럼 a는 local variable임에도 불구하고 영속성을 갖는다. 이게 클로져이다.
JavaScript에서는 함수가 처음 선언 될 때 var a가 hoisting 되고, 함수가 계속 존재하는 한 var a도 지속되어진다.
a는 outer scope에 속한다. inner scope는 outer scope에 대한 참조가 있다. fnc는 inner scope를 가리키는 변수다. fnc가 지속되는 한 a또한 사라지지 않고 존재한다. closure 안에 있다.
이를 좀더 유식하게 말하면 "특정 함수가 참조하는 변수들이 선언된 렉시컬 스코프(lexical scope)는 계속 유지되는데, 그 함수와 스코프를 묶어서 closure 라고한다.
실질적인 closure에 관한 예제
먼저, 우리가 버튼을 클릭할때마다, 버튼이 클릭된 횟수를 세는 기능을 만든다고 가정해보자.
<button onclick="updateClickCount()">click me</button>
1) Global variable을 쓰면 가장 쉽게 해결가능하다.
var counter = 0;
function updateClickCount() {
++counter;
// do something with counter
}
그러나 counter라는 글로벌변수는 쉽게 노출되며, updateClickCount라는 함수의 호출없이 ++ 시킬수 있다.
2)그러면 counter function scope 내부로 옮긴다면?
function updateClickCount() {
var counter = 0;
++counter;
// do something with counter
}
counter는 항상 1로 될것이다. counter는 persistance한 local variable 이 아니기 때문이다.
3)Nested functions를 쓰면?
function countWrapper() {
var counter = 0;
function updateClickCount() {
++counter;
// do something with counter
}
updateClickCount();
return counter;
}
당신이 외부 scope에서 updateClickCount 함수에 접근할수 있고, counter=0라는 선언을 한번만 호출할 수 있다면, 가장 좋은 방법일 것이다.
정답) Closure가 출격한다면 어떻게 될까? C L O S U R E ! (with IIFE)
var updateClickCount=(function(){
var counter=0;
return function(){
++counter;
// do something with counter
}
})();
위처럼 한다면 우리가 처음 의도한대로 동작할 것이다.
local variable을 영속성을 갖게할수 있고 또 local variable을 객체지향적으로 encapsulation 할 수 있게된다.
이에 더하여 counter변수는 global scope에서 hoisting 되지 않으므로 변수의 충돌 또한 방지해준다.
<script>
var updateClickCount=(function(){
var counter=0;
return function(){
++counter;
document.getElementById("spnCount").innerHTML=counter;
}
})();
</script>
<html>
<button onclick="updateClickCount()">click me</button>
<div> you've clicked
<span id="spnCount"> 0 </span> times!
</div>
</html>
https://stackoverflow.com/questions/2728278/what-is-a-practical-use-for-a-closure-in-javascript
PS. 우리는 let으로 문제를 해결했던 for 문 딜레마도 closure를 통해 해결할 수 있다.
var i = 0;
for(i = 0; i<3; i++) {
(function(index){
cosnole.log(My value : index);
}(i))
}
'To be Developer > JavaScript' 카테고리의 다른 글
Core Javascript - 2 Execution Context (0) | 2020.05.13 |
---|---|
Core Javascript 1 - Data Type (0) | 2020.05.12 |
[javascript] let 과 var 그리고 hoisting (0) | 2019.11.07 |
JavaScript에서 Function.prototype.bind의 의미 (0) | 2019.10.29 |
[JavaScript / ES6][펌] 자바스크립트 배열, 문자열 메서드 총정리 (0) | 2017.11.13 |