본문 바로가기
개발

[Javascript] 클로저(Closure)

by 마스터누누 2017. 5. 30.
728x90
반응형

클로저(Closure)



클로저란 Javascript의 유효범위, Scope를 이용하여 생명 주기가 끝난 외부함수의 변수를 참조하는 방법이다.

외부함수가 종료되더라도 내부함수가 실행되는 상태이면 내부함수에서 참조하는 외부 함수는 닫히지 못한다.

따라서 외부 함수가 종료되기 위해서는 내부함수가 종료되어야하기 때문에 클로저(Closure)라고 불리운다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function outFunc(){  
    var num= 0;
    return {
        func1 : function(){
            num+=1;
            console.log("num :"+num);
        },
        func2 : function(){
            num+=2;
            console.log("num :"+num);
        }
    };
}
var out = outFunc();  
var out2 = outFunc();  
out.func1();  // 1
out.func2();  // 3
out2.func1(); // 1
out2.func2(); // 3
cs


클로저를 사용하는 이유는 전역 변수의 오/남용이 없는 깔끔한 코드를 작성할 수 있기 때문이다.

앞서 언급했던대로 자바스크립트는 계속해서 발생한 전역 변수에 대한 해답을 찾기위해 노력했다.

ES6에서 제공되는 let과 const 키워드도 이에 대한 해답중 하나이다.


그러나 let과 const가 없을 때는 디자인 패턴으로 극복하는 경우가 있었는데

네임스페이스나 생성자, 모듈패턴의 그 예 이다.

이중 모듈 패턴과 생성자 패턴은 클로저로 구현될 수 있는 패턴이다.


클로저에서 주의 할점은  내부함수에서 참조하는 외부 함수의 변수는 복사(Clone)본이 아닌 값을 직접 참조한다는 것이다.

위의 코드에서도 func1과 func2가 같은 외부 함수의 변수를 참조하고 있으므로 func1이 실행되어도 func2에 영향을 미친다.


두번째로, 외부 함수를 실행하면 내부 함수가 리턴된다.

따라서 외부함수의 리턴값을 여러개 생성하면, 이러한 클로저가 여러개 생성되고, 각 클로저 마다 외부 함수의 변수는

주소 값이 다르므로 영향을 미치지 않는다.

그러므로, out과 out2는 별개의 변수라고 할 수 있다.


이와 같은 특성은 객체와 비슷하다고 할수 있는데, 실제로 자바스크립트에서는 public이나 private 키워드를 제공하지 않으므로

위의 예제 처럼 클로저를 이용하여 외부 함수의 변수를 private로 지정할수 있다.

이러한 패턴을 모듈 패턴이라고 한다.





1
2
3
4
5
6
7
8
9
10
11
function Person(inputname) {  
    this.name = inputname;
    this.getName = function() {
        return this.name;
    };
    this.setName = function(rename) {
        this.name = rename;
    };
}
var obj= new Person("nununununu");  
console.log(obj.getName()); 
cs


그러나 쓸데없는 클로저의 남용은 메모리면에서 큰 비효율을 낳는다.

위의 코드는 객체의 생성자를 정의하여 인스턴스를 생성하는 생성자 패턴이다.

getName과 setName이라는 클로저가 2개나 있는데, 이 상태에서 인스턴스를 만들면 

같은 일을 하는 클로저가 객체마다 생성될 것이다.

이를 클로저의 오남용 이라고한다.


프로그래밍의 발전사에 가장 중요한 철학인 효율성과 중복성의 제거에 비추어 봤을 때도

인스턴스가 생길때마다 클로저가 중복되므로 상당히 좋지 않게된다.





1
2
3
4
5
6
7
8
9
10
11
function Person(inputname) {  
    this.name = inputname;
}
Person.prototype.getName = function() {  
    return this.name;
};
Person.prototype.setName =   function(rename) {            
    this.name = rename;
};
var obj= new Person("nununununu");  
console.log(obj.getName());  
cs


따라서 클로저는 객체의 prototype 안에 저장함으로써 같은 기능을 모든 인스턴스가 공유하는 형태로

코드를 만들어야한다.

이렇게 prototype안에 클로저를 넣으면, 인스턴스가 생성되더라도 중복으로 메모리를 낭비하지않고,

클로저를 참조할 때 생성자 내부 prototype으로 들어오기 때문에, 효율적인 코드가 된다.



참고 : http://www.nextree.co.kr/p7363/

반응형

댓글