Recent Posts
Recent Comments
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Today
Total
관리 메뉴

고리타분한 개발자

함수 (Function) - 클로저와 참조 (Closures and References) 본문

JavaScript/Garden

함수 (Function) - 클로저와 참조 (Closures and References)

sunlee334 2017. 11. 10. 03:39

클로저는 Javascript의 특징중 하나입니다. 클로저를 만들면 클로저 스코프안에서 클로저를 만든 외부 스코프에 항상 접을할 수 있습니다. Javascript에서 스코프는 함수 스코프밖에 없기 때문에 기본적으로는 모든 함수는 클로저가 될 수 있습니다.


private 변수 만들기


function Counter(start) {
var count = start;
return {
increment: function() {
count++;
},

get: function() {
return count;
}
}
}

var monkey = Counter(4);
monkey.increment();
monkey.get(); // 5


여기서 Counter는 increment 클로저와 get 클로저 두 개를 반환합니다. 이 두 클로저는 Counter 함수 스코프에 대한 참조를 유지하고 있기 때문에 이 함수 스코프에 있는 count 변수에 계속 접근할 수 있습니다.


private 변수의 동작 원리


Javascript에서는 스코프를 어딘가에 할당해두거나 참조할 수 없기 때문에 스코프 밖에서는 count 변수에 직접 접근할 수 없습니다. 접근할 수 있는 유일한 방법은 스코프 안에 정의한 두 클로저를 이용하는 방법밖에 없습니다.


var monkey = new Counter(4);
monkey.hack = function() {
count = 1337;
};


위 코드에서 monkey.hack 함수는 Counter 함수 안에서 정의되지 않았기 때문에 이 함수가 실행되더라도 Counter 함수 스코프 안에 있는 count 값은 변하지 않습니다. 대신 monkey.hack 함수의 count는 Global 스코프에 생성되거나 이미 만들어진 변수를 덮어씁니다.


반복문에서 클로저 사용하기


사람들이 반복문에서 클로저를 사용할 때 자주 실수를 하는 부분이 있는데 바로 인덱스 변수를 복사할 때 발생합니다. 


for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}


위 코드는 0부터 9까지의 수를 출력하지 않고 10만 열번 출력하는 함수입니다.

타이머에 설정된 익명 함수는 변수 i에 대한 참조를 가지고 있다가 console.log가 호출되는 시점에 i의 값을 사용합니다. console.log가 호출되는 시점에서 for loop는 이미 끝난 상태이기 때문에 i 값은 10이 됩니다.


앞의 참조 문제 해결하기


반복운의 index값을 복사하는 가장 좋은 방법은 익명함수로 랩핑하는 방법입니다.


for (var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}


이 익명 함수에 i를 인자로 넘기면 이 함수의 파라미터 e에 i의 값이 복사되어 넘어갈 것입니다. 그리고 setTimeout은 익명 함수의 파라미터인 e에 대한 참조를 가지게 되고 e값은 복사되어 넘어왔으므로 loop상태에 따라 영향을 받지 않습니다.

또다른 방법으로 랩핑한 익명 함수에서 출력 함수를 반환하는 방법도 있습니다.


for (var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}


자주 쓰이는 또 하나의 방법은 setTimeout 함수에 세번째 인자를 추가하는 방법입니다. 추가된 인자는 콜백 함수에 전달됩니다.


for (var i = 0; i < 10; i++) {
setTimeout(function(e) {
console.log(e);
}, 1000, i);
}


.bind를 사용하여 원하는 결과를 얻는 방법도 있습니다. .bind는 this 컨택스트와 인자들을 함수에 바인딩 시킵니다. 


for (var i = 0; i < 10; i++) {
setTimeout(console.log.bind(console, i), 1000);
}


License

이 게시글은 JavaScript Garden을 참고하여 작성 되었습니다. (https://github.com/BonsaiDen/JavaScript-Garden)








Comments