티스토리 뷰

클로저
클로저 - 생활코딩

Closure

Mozilla reference로는 '함수와 함수가 선언된 어휘적(Lexical) 환경의 조합'을 의미.

쉽게 설명하면, 1) 어떤 함수내에서 함수를 정의하고 사용하는 것과 2) 이때 내부함수에서 외부함수에 접근할 수 있는 원리를 의미하는 정도로 요약해본다.

function outter(){
    var title = 'coding everybody';  
    function inner(){        
        alert(title);
    }
    inner();
}
outter(); //coding everybody

함수안에서 함수를 정의하고 사용한다. var inner = function() { ~ }와 같으니 문제는 없다.

inner라는 내부함수에서 title변수를 사용하는데, 찾아보니 inner함수안에 없다. 따라서 외부함수 outter에서 찾는다. 발견한 title값을 사용해 출력.

즉, 내부함수는 외부함수의 지역변수에 접근할 수 있다.

function outter(){
    var title = 'coding everybody';  
    return function(){        
        alert(title);
    }
}
inner = outter();
inner(); //coding everybody

위의 예제를 변경. inner = outter();에서 우변의 리턴값으로 이름없는함수가 넘어오고 outter함수는 실행이 끝난다. 넘어온 결과를 inner에 넣는다.

이후, inner()를 실행하면 'coding everybody'가 출력된다?! <= outter함수는 실행이 끝난 시점이기 때문에 title 지역변수가 소멸되었을것으로 생각할수있으나, 그렇지 않다. 살아있다. (역시 타언어와 다른점)

function factory_movie(title){
    return {
        get_title : function (){
            return title;
        },
        set_title : function(_title){
            title = _title
        }
    }
}
ghost = factory_movie('Ghost in the shell');
matrix = factory_movie('Matrix');

alert(ghost.get_title()); //Ghost in the shell
alert(matrix.get_title()); //Matrix

ghost.set_title('공각기동대');

alert(ghost.get_title()); //공각기동대
alert(matrix.get_title()); //Matrix

ghost와 matrix가 각자 만들어진 (Lexical) 환경을 기억하고 유지함을 주목하자.

위 예제의 결과까지 참고하면, 결국
이런 클로저의 특성을 이용해 'Private한 접근이 가능해짐'을 알수있다. (Private 속성(데이터 은닉, 캡슐화) 사용 가능)
(이렇게 클로저를 사용해 private 개념을 구현하는것을 디자인패턴에서 module pattern이라 한다)

이런식으로 대개 정의한 함수를 리턴시켜서 바깥에서 사용하는 방식으로 사용된다.

JavaScript 클로저(Closure)


자주 실수하는 예제

var arr = []
for(var i = 0; i < 5; i++){
    arr[i] = function(){
        return i;
    }
}
for(var index in arr) {
    console.log(arr[index]());
}
//55555

의외로 01234가 아닌 55555가 출력된다.

일단 arr[0]-arr[4]안에는 각각 return i;가 실행된 결과 리턴값이 저장되는것이 아니라, function() {-} 함수 자체가 통째로 저장된다. 즉 아직까진 함수가 정의만되고 사용되진 않는다.

여기서 5만 다섯번 출력되는 이유는, return i의 i는 외부함수가 없어 무엇인지 인식하지못하고있는것이다.
즉 for문의 i++값을 정상적으로 인식하고 가져오지못한다.
따라서 arr[0~4]안에 통째로 함수로서 들어갈때도 return 0;, return 1;, ... return 4;로 i가 숫자로 들어가는것이 아니라! return i;, return i;, ... return i;로 i 그자체로 들어간다.

이후 console.log에서 실제 사용되었을때, arr의 0번째안의 값이 호출되고, 이때 function함수가 실행되는데, 이미 첫번째for문을 다돈지 오래이므로 변수i의 값은 5이다. 따라서 return 5;가 되고 5가 출력된다.
다음 arr의 1번째, 2번쨰, 3번째, 4번째 값이 호출될때도 당연히 마찬가지이므로 5가 출력된다.
(함수가 사용이 아닌 정의될때의 scope가 적용되는것으로 알고있었으나, 여기선 정의될때 외부함수가 존재하지않고 제대로 인식하지못했으므로 i상태로 남았고 이후 나중에 i가 다시 인식된것으로, 현재까지는 이해...! //이후 틀린부분있으면 수정하기)

최종적으로, 정상적으로 출력시키기위해 아래 코드와 같이 클로저를 적용하여 고친다.

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]());
}
//01234

값 전달 과정 : i++(0,1,2,3,4) => }(i);의 i => function(id)의 id => return id;의 id => arr[i]

이해에 도움이 되는 예제

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
</head>
<body>
    <script>
        var arr = []
        console.log("START");
        for(var i = 0; i < 5; i++){
            console.log("A");
            arr[i] = function(){
                console.log("B");
                console.log("i : "+i);
                return i;
            }
        }

        console.log("[1st for문 END] final return값 i : "+i);
        console.log("[2nd for문 START]");

        for(var index in arr) {
            console.log("C");
            console.log("index : "+index);
            console.log("arr : "+arr[index]());
        }
        console.log("END");
    </script>
</body>
</html>

출력결과

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함