본문 바로가기
프로그래밍 개발/JS 기본 언어

Javascript 클로저

by Jinseok Kim 2020. 11. 10.
반응형

 

 

클로저이란?

 

 

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

 

 

 

 

 

 

 

 

내부함수

 

 

function outter(){
    function inner(){
        var title = 'jin seok'; 
        alert(title);
    }
    inner();
}
outter();

 

 

  • 자바스크립트는 함수 안에서 또 다른 함수를 선언할 수 있다.
  • 위의 예제에서 함수 outter의 내부에는 함수 inner가 정의 되어 있다. 함수 inner를 내부 함수라고 한다.

 

 

 

 

 

 

function outter(){
    var title = 'jin seok';  
    function inner(){        
        alert(title);
    }
    inner();
}
outter();

 

  • 내부함수는 자신의 함수 안에 지역변수가 없으면 자신을 감싸고 있는 외부함수의 지역변수에 접근할 수 있다.
  • 내부함수 inner에서 title을 alert(title)로 호출했을 때 외부함수인 outter의 지역변수에 접근할 수 있음을 보여준다.

 

 

 

 

 

 

 

 

 

 

 

클로저

 

 

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

 

 

function outter(){
    var title = 'jin seok';  
    return function(){        
        alert(title);
    }
}
inner = outter();
inner();

 

  • inner = outter();에서 함수 outter를 호출하고 있다. 그 결과가 변수 inner에 담긴다. 그 결과는 이름이 없는 함수다.
  • 실행이 함수 inner();으로 넘어오면 outter 함수는 실행이 끝났기 때문에 이 함수의 지역변수는 소멸되는 것이 자연스럽다.(함수에서 딱 한번 return값을 실행하면 값은 소멸된다.)
  • 하지만 함수 inner();를 실행했을 때 jin seok가 출력된 것은 외부함수의 지역변수 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());
alert(matrix.get_title());
 
ghost.set_title('공각기동대');
 
alert(ghost.get_title());
alert(matrix.get_title());

 

 

1. 클로저는 객체의 메소드에서도 사용할 수 있다. 위의 예제는 함수의 리턴값으로 객체를 반환하고 있다. 이 객체는 메소드 get_title set_title을 가지고 있다. 이 메소드들은 외부함수인 factory_movie의 인자값으로 전달된 지역변수 title을 사용하고 있다.

 

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

 

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

 

4. 즉 factory_movie의 지역변수 title은 2행에서 return으로 정의된 객체의 메소드에서만 접근 할 수 있는 값이다. 이 말은 title의 값을 읽고 수정 할 수 있는 것은 factory_movie 메소드를 통해서 만들어진 객체 뿐이라는 의미다.

 

5. JavaScript는 기본적으로 Private(전용, 사유)한 속성을 지원하지 않는데, 클로저의 이러한 특성을 이용해서 Private한 속성을 사용할 수 있게된다.

 

※ Private 속성은 객체의 외부에서는 접근 할 수 없는 외부에 감춰진 속성이나 메소드를 의미한다. 이를 통해서 객체의 내부에서만 사용해야 하는 값이 노출됨으로서 생길 수 있는 오류를 줄일 수 있다. 자바와 같은 언어에서는 이러한 특성을 언어 문법 차원에서 지원하고 있다.

 

 

 

 

 

 

 

 

 

 

 

클로저에서 자주 언급되는 심화 예제

 

 

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

위의 코드는 함수가 함수 외부의 컨텍스트에 접근할 수 있을 것으로 기대하며 0~4가 출력될 것을 예상했지만 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]());
}

  • 위의 예제는 외부함수, 내부함수의 성질을 이용한 클로저를 사용하여 원하던 결과대로 나오게 되었다.
  • 처음 코드의 문제가 뭐였냐면 for문의 인수인 i값function()의 외부변수 값이 아니기 때문이었다. 왜냐하면 arr[i]function(){return i;}를 의미하고 그 함수에 들어있는 변수 i는 for 반복문이 다 돌고 나서 5로 정의된 전역변수 i를 데리고 온 것이라서 문제가 생긴 것이다. 
  • 처음 예상한 것처럼 0~4 결과값을 만들고 싶다면 클로저를 이용해야 한다. iarr[i]에 들어가서 arr[0~5]를 만들되 그 안에서 실행되는 함수는 for문에서 정의된 i=0; i<5; i++의 영향을 받지 않으려면 외부함수를 만들어 외부함수의 지역변수를 따로 정의하고, 내부함수의 변수가 for문을 도는 전역변수 대신 지역변수값을 끌어다 쓰게 해야 한다.
  • 수정 방법은 외부함수로 정의되지 못한 함수 function()에 내부함수를 참조하게 해서 외부함수로 정의하게 하여 만들어내었다. 하지만 외부함수를 따로 만들었다고 해도 이대로라면 for문의 i대신 따로 정의 내린 외부함수의 지역변수가 없으니 내부함수의 변수 i는 계속 for문에 정의되어 있는 전역변수 i값을 끌어다 쓸 것이다. 따라서 외부함수의 매개변수를 id로 바꿔주며 새롭게 지역변수를 정의한다. 
  • 그리고 외부함수를 정의하기 위해 일부러 참조했던 내부함수의 return 값에 id을 붙여 외부함수의 지역변수이자 매개변수 id의 값을 참조하도록 하였다.
  • 외부함수의 인수인 지역변수이자 매개변수 id 'arr[i] =' 을 통해 i값을 받아서 외부함수의 내부인 내부함수매개변수 id을 붙여주어 i값을 전달할 수 있게되었다. 전달과 동시에 내부함수 idreturn하게 되자 이 내부함수외부함수지역변수이자 매개변수 id을 접근하여 쓸 수 있게 되었다.
  • 그리고 외부함수를 즉석으로 호출하기 위해 외부함수의 인자값으로 i를 설정한다.
  • 그 결과 내부함수외부함수의 지역변수이자 매개변수인 id로부터 내려온 i값을 받아내어 return 값을 내게 되어 0~4까지 결과를 출력할 수 있게 되었다. 즉 이제 for문을 돌지 않은 i값을 내보내게 되어 외부함수의 함수인자에 i를 집어넣어도 전역변수 i가 아니라 외부함수의 지역변수 id의 값이 되기 때문이다.

 

 

 

 

 

 

 

 

반응형

댓글