Programing/JavaScript

클로저 사용시 주의할 점. 클로저 유의사항, 반복문, setInterval, 실행시점

리커니 2017. 5. 25.
반응형

클로저 사용시 주의할 점. 클로저 유의사항, 반복문, setInterval, 실행시점

 

(그 전에 클로저에 대해 알고싶으신 분들은 아래의 Link를 참고하세요.)

 

Link : javascript closure 자바스크립트 클로저의 개념 쉽게 이해. 클로저란?

 

javascript closure 자바스크립트 클로저의 개념 쉽게 이해. 클로저란?

javascript closure 자바스크립트 클로저의 개념 쉽게 이해. 클로저란? 자바스크립트 하면 빠지지 않는 것 중에 하나가 클로저(Closure) 입니다. 하지만 이것을 사용하지 않는다고 해서 개발을 못하진 않죠. 그렇..

aljjabaegi.tistory.com

 

[[1. 반복문에서의 클로저]]

 

예를 보면서 설명 하겠습니다.

1
2
3
4
5
6
7
8
9
 var arr = [];
 for (var i=0; i<5; i++){
      arr[i] = function(){
           return i;
      }
 }
 for(var index in arr){
      console.log(arr[index]());
 }
cs

간단한 코드 입니다.

0-4까지 반복을 하면서 i 값을 리턴하는 익명함수를 배열에 넣어

그 배열의 크기만큼 함수를 실행시켜 콘솔에 값을 찍는 코드 입니다.

 

결과를 예측 하셨나요?

a[0]=0, a[1]=1, a[2]=2, a[3]=3, a[4]=4 가 들어갈꺼니깐, 0 1 2 3 4 가 찍힐꺼야.

 

0 1 2 3 4 가 찍힐꺼야! 대부분 이렇게 생각하시겠죠.

 

하지만 클로저 사용 시 에는 주의 해야 할 점이 있습니다.

반복문에서 클로저 사용 시 반복문이 끝날 시점의 값이 클로저가 된다는 것입니다. 

뭐가 클로저냐구요???

1
2
3
4
5
 for (var i=0; i<5; i++){
      arr[i] = function(){
           return i;
      }
 }
cs

위 코드에서 i 가 클로저가 됩니다.

익명함수 안에 return i를 하는데 이 익명함수 안에는 아무리 찾아봐도 i 가 없습니다.

그러니 스코프 체인에 의해 상위에서 i를 찾게 되죠.

하지만 클로저의 실행시점이 for문이 끝난 시점의 값이기 때문에 반복문이 끝난 후의 값인 5가 됩니다.

 

결과는 0 1 2 3 4 가 아니라 5 5 5 5 5 가 되는 것이죠.

그럼 정말 그런지 한번 확인해 볼까요? 아래와 같이 콘솔을 찍어보세요.

1
2
3
4
5
6
7
8
9
10
11
12
var arr = [];
 for (var i=0; i<5; i++){
      console.log('i out function : '+i);
      arr[i] = function(){
           console.log('i in function :'+i);
           return i;
      }
 }
 console.dir(arr);
 for(var index in arr){
      console.log(arr[index]());
 }
cs

 

실행되는 시점이 보이시죠??

익명함수 밖의 i 값이 먼저 다 찍힌 후에 익명함수 안의 i값(클로저)가 찍히게 됩니다.

그러니 for문이 실행되고 난 후의 값인 5에 클로저가 접근하게 되는것이죠.

 

 

 

배열에서의 클로저를 확인해봐도 모든 익명함수의 i값은 5가 됩니다.

 

그럼 우리의 예상대로 0 1 2 3 4 를 찍기 위해선 어떻게 해야 할까요?

반복문이 끝난 시점에서 클로저가 실행되니, 반복문이 돌때 클로저가 실행되도록 하면 되겠죠?

 

위의 코드를 아래와 같이 변경해 줍니다.

1
2
3
4
5
6
7
8
9
10
11
var arr = [];
for (var i=0; i<5; i++){
     arr[i] = function(id){
          return function(){
               return id;
          }
     }(i);
}
for(var index in arr){
     console.log(arr[index]());
}
cs

즉시 실행함수를 만들어서 for문이 실행될때 익명함수도 실행되도록 하면 됩니다.

 

 

 

이렇게 하면 원하는 0 1 2 3 4 를 찍을 수 있게되죠.

 

 

console.dir(arr); 을 해보면 각 function에 closure 가 0, 1, 2, 3, 4 가 되는 것을 알 수 있습니다.

 

이처럼 반복문 내에서 클로저를 사용할 경우 실행시점을 이해하고 작성을 해야 합니다~

주의하세요~

 

[[2. setInterval() 사용 시 클로저]]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function test(obj, time){
    if(obj.isArray()==true){  //or typeof obj!='function'
        var returnIntervalArray = new Array();
        for (var i in obj){
            obj{i}();
            var timeFunc = function(j){
                returnIntervalArray[j] = setInterval(function(){
                    obj[j]();
                }, time);
            }(i);
        }
        return returnIntervalArray;
    }else{
        obj();
        var returnInterval = setInterval(function(){
            obj();
        }, time);
        return returnInterval;
    }
});
cs

위의 소스르 보면 매개변수인 obj 에 따라서 다른 로직을 타고 있습니다.

그 이유는 함수가 들어있는 배열을 매개변수로 받을 수도 있고, 함수 하나만을 매개변수로 받을 수 있게끔 구현하고 싶었기 때문이죠.

 

그래서 obj.isArray()로 (or typeof obj =='function') 함수인지 배열인지 확인을 해서 다른 로직을 타게 했죠.

우선 함수로 넘어왔을 경우를 보도록 하죠.

 

obj(); 우선 함수를 한번 실행을 하고

setInterval 을 사용해 매개변수로 넘어온 함수를 time 매개변수 시간만큼 반복하겠다 입니다.

별거 없죠. 기본적인 setInterval의 사용법입니다. (returnInterval을 하는 이유는 stop 하기 위함)

 

그럼 배열일 경우를 보도록 하죠.

var returnIntervalArray = new Array()로 새 배열을 생성합니다.

그리고 넘어온 for in 문을 써서 배열을 갯수만큼 반복을 하죠.

 

우선 배열에 있는 함수를 실행합니다. 그리고 함수가 넘어왔을 때와 같이 setInterval을 time 시간 만큼 반복하죠.

하지만 여기서 timeFunc는 즉시 실행 함수를 사용했습니다.

1
2
3
4
5
var timeFunc = function(j){
    returnIntervalArray[j] = setInterval(function(){
        obj[j]();
    }, time);
}(i);
cs

이부분이 중요한데요.

위의 반복문에서의 클로저에서

반복문에서 클로저 사용 시 반복문이 끝날 시점의 값이 클로저가 된다는 것입니다. 

라고 설명을 했었죠.

 

같은 이유 때문입니다. 즉시 실행 함수로 하지 않으면 반복문이 끝날 시점의 값이 클로저가 되기 때문에

obj로 넘어온 배열의 마지막 함수만이 time 마다 반복이 되게 되는 것이죠.

 

이처럼 클로저의 실행시점에 유념해서 사용해야 합니다~

 

반응형

댓글

💲 추천 글