[JavaScript] 클로저


클로저


클로저(closure)는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가르킨다. 클로저는 자바스크립트를 이용한 고난이도의 테크닉을 구사하는데 필수적인 개념으로 활용된다.  


1. 내부함수

자바스크립트는 함수 안에서 또 다른 함수를 선언할 수 있다. 아래의 예제를 보자. 결과는 경고창에 coding everybody가 출력될 것이다.

function outter(){// 외부 함수

    function inner(){ //내부 함수

        var title = 'coding everybody'; 

        alert(title);

    }

    inner();

}

outter();

실행결과)

coding everybody

위의 예제에서 함수 outter의 내부에는 함수 inner가 정의 되어 있다. 함수 inner를 내부 함수라고 한다.


내부함수는 외부함수의 지역변수에 접근할 수 있다.  이것이 바로 클로저이다. 아래의 예제를 보자.

function outter(){

    var title = 'coding everybody';

    function inner(){        

        alert(title);

    }

    inner();

}

outter();

실행결과)

coding everybody

위의 예제는 내부함수 inner에서 title을 호출(4행)했을 때 외부함수인 outter의 지역변수에 접근할 수 있음을 보여준다.


2. 클로저


클로저(closure)는 내부함수와 밀접한 관계를 가지고 있는 주제다. 내부함수는 외부함수의 지역변수에 접근 할 수 있는데 외부함수의 실행이 끝나서 외부함수가 소멸된 이후(return)에도 내부함수가 외부함수의 변수에 접근 할 수 있다. 

이러한 메커니즘을 클로저라고 한다. 아래 예제는 이전의 예제를 조금 변형한 것이다.

function outter(){

    var title = 'coding everybody';  

    return function(){ // return했다면 그 함수는 생을 마감했다는 것이다.       

        alert(title);// title은 외부함수에 존재하는 값이다.

    }

}

inner = outter();//outter라는 함수가 생을 마감했음에도 불구하고

inner();

실행결과)

coding everybody

예제의 실행순서를 주의깊게 살펴보자. 7행에서 함수 outter를 호출하고 있다. 

그 결과가 변수 inner에 담긴다. 그 결과는 이름이 없는 함수다. 실행이 8행으로 넘어오면 outter 함수는 실행이 끝났기 때문에 이 함수의 지역변수는 소멸되는 것이 자연스럽다. 

하지만 8행에서 함수 inner를 실행했을 때 coding everybody가 출력된 것은 외부함수의 지역변수 title이 소멸되지 않았다는 것을 의미한다. (title은 외부함수의 지역변수이다.)

클로저란 내부함수가 외부함수의 지역변수에 접근 할 수 있고, 외부함수는 외부함수의 지역변수를 사용하는 내부함수가 소멸될 때까지 소멸되지 않는 특성을 의미한다.


조금 더 복잡한 아래 예제를 살펴보자. 아래 예제는 클로저를 이용해서 영화의 제목을 저장하고 있는 객체를 정의하고 있다. 

function factory_movie(title){ // 매개변수는 함수 안에서 지역변수로 사용되기 때문에 지역변수이다.

    return {

        get_title : function (){// 여기 메소드들은 factory_movie의 내부함수라고 보면된다.

// 변수 가져오기는 get_title을 통해서만

            return title;// factory_movie의 인자인 title이다.

        },

        set_title : function(_title){

// 변수 수정하는 것은 set_title을 통해서만 --> 보다 변수를 안전하게 사용할 수 있다.

            title = _title

        }

    }

}

ghost = factory_movie('Ghost in the shell'); 

matrix = factory_movie('Matrix');

 

alert(ghost.get_title()); 

alert(matrix.get_title());

// 각각 get_title()이라는 메소드가 접근하는 외부함수의 지역변수에 담겨있는 값이 서로 다르다.

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

 

alert(ghost.get_title());

alert(matrix.get_title());

실행결과)

Ghost in the shell -> Matrix -> 공각기동대 -> Matrix 


위의 예제를 통해서 알 수 있는 것들을 정리해보면 아래와 같다.

1) 클로저는 객체의 메소드에서도 사용할 수 있다. 위의 예제는 함수의 리턴값으로 객체를 반환하고 있다. 

이 객체는 메소드 get_title과 set_title을 가지고 있다. 이 메소드들은 외부함수인 factory_movie의 인자값으로 전달된 지역변수 title을 사용하고 있다.


2) 동일한 외부함수 안에서 만들어진 내부함수나 메소드는 외부함수의 지역변수를 공유한다. 17행에서 실행된 set_title은 외부함수 factory_movie의 지역변수 title의 값을 '공각기동대'로 변경했다. 19행에서 ghost.get_title();의 값이 '공각기동대'인 것은 set_movie와 get_movie 함수가 title의 값을 공유하고 있다는 의미다.


3) 그런데 똑같은 외부함수 factory_movie를 공유하고 있는 ghost와 matrix의 get_title의 결과는 서로 각각 다르다. 그것은 외부함수가 실행될 때마다 새로운 지역변수를 포함하는 클로저가 생성되기 때문에 ghost와 matrix는 서로 완전히 독립된 객체가 된다.


4) factory_movie의 지역변수 title은 2행에서 정의된 객체의 메소드에서만 접근 할 수 있는 값이다. 이 말은 title의 값을 읽고 수정 할 수 있는 것은 factory_movie 메소드를 통해서 만들어진 객체 뿐이라는 의미다. JavaScript는 기본적으로 Private한 속성을 지원하지 않는데, 클로저의 이러한 특성을 이용해서 Private한 속성을 사용할 수 있게된다.


cf.)

Private 속성은 객체의 외부에서는 접근 할 수 없는 외부에 감춰진 속성이나 메소드를 의미한다. 이를 통해서 객체의 내부에서만 사용해야 하는 값이 노출됨으로서 생길 수 있는 오류를 줄일 수 있다. 

자바와 같은 언어에서는 이러한 특성을 언어 문법 차원에서 지원하고 있다. 아래의 예제는 클로저와 관련해서 자주 언급되는 예제다. 

var arr = [];

for(var i = 0; i < 5; i++){

    arr[i] = function(id){

        return function(){

            return id;

        }

    }

}

for(var index in arr) {

    console.log(arr[index]());

}

실행결과)

1

2

3

4

5

5

5

5

5

5


위의 코드는 아래와 같이 변경해야 한다.

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

}

실행결과)

0

1

2

3

4


※ 클로저 참고

https://developer.mozilla.org/ko/docs/JavaScript/Guide/Closures

http://ejohn.org/apps/learn/#48

http://blog.javarouka.me/2012/01/javascripts-closure.html

'WebFont-end > JavaScript' 카테고리의 다른 글

[JavaScript] Object Model  (0) 2014.12.21
[JavaScript] arguments(유사 배열 객체)  (0) 2014.12.21
[JavaScript] 유효범위  (0) 2014.12.21
[JavaScript] Object  (0) 2014.12.07
[JavaScript] prototype  (0) 2014.12.07