클로저는 자신의 범위(Scope) 밖에 있는 변수들에 접근할 수 있는 함수를 의미합니다.
사실 이 말만 가지고는 잘 감이 오지 않고 보통 자바스크립트내에서는 함수의 생명주기는 끝이났지만 함수내의 변수를 내부함수가 참조하고 있기 때문에 유지되어 접근할수 있는 함수를 클로저라고 합니다.
<input type="button" id="btn1"/>
<input type="button" id="btn2"/>
<input type="button" id="btn3"/>
<input type="button" id="btn4"/>
위와 같이 버튼이 4개 있고 각 버튼을 클릭했을때 각 버튼당 1,2,3,4가 찍히게 하고 싶다고 하겠습니다. 당연히 가장 쉬운 방법은 각 버튼에 인라인으로 onclick="alert('1')" 처럼 각 버튼당 파라미터를 주는 것이 쉽겠지만 이럴 경우 요즘 일반적인 구조와 동작을 불리하는 Unobtrusive Javascript에도 맞지 않고 유지보수에도 별로 좋지 않습니다.
일반적으로 사람들이 위와같은 구현을 하기 위해서 가장 먼저 시도하는 코드는 아래와 같을 것입니다.
window.onload = function() {
for(var i=1; i < 5; i++ ) {
document.getElementById("btn" + i).addEventListener("click", function() {
alert(i);
}, false);
}
}
모두는 아니겠지만 보통 위와같은 코드를 시도하리라고 생각하고 정상적으로 동작할 것을 기대하지만 위 코드는 제대로 동작하지 않습니다. for문을 돌면서 각 버튼에 click이벤트리스너를 등록하고 각 루프에서의 i를 alert으로 보여줍니다. 이렇게 할경우 의도한 것은 1,2,3,4의 alert()을 의도한것이지만 alert()에 넘겨준 파라미터는 i의 값이 아닌 i의 참조이기 때문에 실제 버튼을 클릭하면 모든 버튼을 클릭할 때 i의 최종값이 5가 모두 찍혀버립니다.
이 상황이 클로저가 적합한 상황인데 클로저를 사용하는 것은 이해만 하면 그렇게 어렵지 않습니다.
window.onload = function() {
for(var i=1; i < 5; i++ ) {
(function(m) {
document.getElementById("btn" + m).addEventListener("click", function() {
alert(m);
}, false);
})(i);
}
}
위와 같이 작성합니다. for문안에 실행할 구문을 익명함수( (function() {})와 같은 형태)로 만들고는 i를 파라미터로 넘기면서 실행시켜버립니다.(익명함수에 (i)를 붙혀서 바로 실행시켰습니다.) 이렇게 하면 익명함수안의 내부변수인 m에 i의 값이 할당되어 버리고 구조상은 실행뒤에 소멸되어야 하지만 클로저로 인하여 각 값으로 할당된 멤버변수를 각 이벤트리스너에서 그대로 사용할 수 있게 됩니다. 위 코드로 실행하면 각 버튼을 클릭시 1,2,3,4의 원하던 값이 찍히게 됩니다.
덧) 그냥 예제코드이기 때문에 표준인 addEventListener만을 사용했습니다. IE에서 돌려보실 계획이라면 attachEvent를 사용하셔야 합니다.
덧2) 제가 클로저의 개념을 아주 명확히 파악하지 못한관계로 설명이 명확치 않았습니다. 위 소스에 대한 명확한 설명은 아래 댓글에 odyss님이 해주셨으므로 참고해주시기 바랍니다. 공부를 더 열심해야겠군요. ㅠㅠ
약간 보충설명을 하자면,
첫번째 예제는 클로저의 생성으로 인한 부작용을 보여줍니다.
원래 의도는 각 버튼마다 alert시에 1,2,3,4를 결과로 보여주려는 의도이나 이벤트 핸들러 함수의 i값이 바깥쪽 변수인 i값에 대한 참조를 유지하고 있어, 즉 클로저의 생성으로 인해 최종값인 5를 모두 가리키게 되는 예제입니다.
사실 두번째 예제는 클로저의 부작용을 막기위한 처리로 제시한 예제인데, 이 예제도 클로저가 생성되긴 합니다만 익명함수의 인자로 값을 넘겨버림으로써 바깥쪽 변수인 i에 대한 변수스코프를 끊어버리고, 이벤트 핸들러에서는 익명함수의 인자값에 접근함으로써 의도한 대로 처리가 되게 됩니다.
괄호로 둘러싼 함수표현식 안에서는 바깥쪽 변수에 접근하지 못한다는 것을 여기서 아실 수 있습니다.
그렇군요. 명확한 설명 감사합니다.
저도 아직 클로저는 완전히 이해하지 못해서 설명하는데 좀 힘들었는데 명확하게 설명해주시니 아주 좋군요. 일부 저도 헷갈리는 부분이 좀 있었는데요.. ㅎㅎㅎ
댓글에 약간의 태클을 걸자면, 위 예제에서 괄호로 둘러싸인 함수가 외부 변수에 접근할 수 있습니다;
함수가 특정 오브젝트의 실행컨텍스트로 실행될 때..그러니까 메소드로 실행될 때 메소드 내부에 있는 함수의 컨텍스트는 글로벌이 된다는 것을 혼동하신거 같네요...
제가 예제를 잘못들어서요.. ㅎㅎㅎ
전 트랙백 달구 후다닥 ==3=3=3
좋은 설명 감사합니다. ㅎ
개념은 이해될 것 같은데 응용해서는 더 수련해야겠네요 ㅎㅎ
관리자만 볼 수 있는 댓글입니다.
settimeout에서 쓰면되는건뎅;
settimeout에서 어떻게 쓴다는 건가요?
이야~~~ 제가 딱 찾던 글이네요 포스팅 정말 감사드립니다.!!!
저도 헷갈려하면서 정리했던것데 도움되셨다니 다행이네요 ㅎ
담아갈게요~
왜...저는 차단되서 댓글을 달 수가 없나요...?
댓글 달렸는데 다른데서 안된다는 말씀이신가요? 스팸필터에 걸리는것 같은데 어떤 글이 안되는지 따로 알려주시면 확인해 보겠습니다.
잘보고 갑니다 ^^
잘보고 갑니다. 클로저를 이해하는데 큰 도움이 되었습니다 :)
댓글도 같이 보셔야 명확입니다. 저도 잘 모를때 적은거라서요.
window.onload = function() {
for(var i=1; i < 5; i++ ) {
(function(i) {
document.getElementById("btn" + i).addEventListener("click", function() {
alert(i);
}, false);
});
}
}
이렇게 써도 되는거죠?
익명함수에 (function(i) {})(i); 이렇게 실행을 해주어야 합니다. 주신 코드는 선언만 하고 실행을 안해서 동작하지 않습니다.