본문 바로가기

Programming/Javascript

12. Javascript 클로저(Closure)(2) - 클로저의 활용 1

javascript logo

 

 

지금까지 알아본 클로저의 개념을 잘 이해하고 따라오셨나요? 그렇다면 이제 이 클로저라는 특별한 함수를 어떻게 사용하는지, 그 예제를 함께 확인해 볼 차례입니다. 우선, 아래와 같은 예제 코드가 있다고 해봅시다.

 

function HelloFunc() {
    this.greeting = "hello_World!";
}

HelloFunc.prototype.call = function(func) {
    func ? func(this.greeting) : this.func(this.greeting);
};

var userFunc = function(greeting) {
    console.log(greeting);
};

var ojbHello = new HelloFunc();
ojbHello.func = userFunc;
ojbHello.call();

 

위 예제에서, 생성자 함수 HelloFunc( )가 선언되고 prototype에서의 정의를 통해 call( ) 함수에 인자로 전달된 함수를 호출하거나 또는 HelloFunc 객체에 사용자가 직접 선언한 func를 호출하는 형태로 선언되었습니다. 물론 이 func를 실행할 때는 인자로 HelloFunc를 통해 생성된 객체가 가진 greeting이라는 프로퍼티를 인자로 사용합니다. 

 

이 함수는 정상적으로 "hello_World!"가 출력되는 것을 확인할 수 있습니다. 그런데, 이 함수를 자세히 살펴보면, call( )에서 정의된 내용을 보면 this.greeting이라는 인자 1개 만을 받아서 사용자가 정의한 함수를 실행합니다. 그러므로, 사용자가 정의한 함수(userFunc)의 파라미터도 단 한 개 만을 정의하고 있습니다. 이제 사용자가 원하는 인자를 추가로 넣어서 실행하는 방법이 있을지 살펴보도록 하겠습니다.

 

 

function HelloFunc() {
    this.greeting = "hello_World!";
}

HelloFunc.prototype.call = function(func) {
    func ? func(this.greeting) : this.func(this.greeting);
};

var ojbHello = new HelloFunc();

function saySomething(obj, methodName, name) {
    return (function(greeting) {
        return obj[methodName](greeting, name);
    });
}

function newObj(obj, name) {
    obj.func = saySomething(this, "who", name);
    return obj;
}

newObj.prototype.who = function(greeting, name) {
    console.log(greeting + " " + (name || "everyone") );
};

var obj1 = new newObj(ojbHello, "zzoon");
obj1.call();

 

식이 다소 복잡해졌습니다. 위의 예제 코드에서 일부 라인의 실행문을 제거하고 작성한 내용입니다. 이번에는 다소 내용이 복잡합니다. 우선, 결론만 말하면 위의 saySomething에서 반환되는 function(greeting) { ... } 라인이 클로저로서 작동합니다. 

 

newObj는 예외적이지만 생성자 함수로 사용했습니다. 그래서 obj.func에 할당되는 saySomething( )함수에서 첫 번째 인자로 this가 사용되는데, 생성자 함수이기 때문에(new 키워드 사용) 여기서 this는 newObj 객체를 통해 생성되는 객체를 의미하게 됩니다. 단, 실제로 리턴은 obj를 리턴하게 되므로 주의해야 합니다. 그래서 saySomething 함수의 첫 번째 인자는 newObj { } 객체가 됩니다. 만일 일반적인 생성자 함수 사용 패턴에서처럼, [this.프로퍼티 = 매개변수]의 형태였다면 특정 속성을 가진 newObj 객체가 바인딩되겠지만, 여기서는 단순히 newObj{ } 객체가 할당된다고 이해하시면 됩니다. 결과적으로 saySomething( ) 함수에서는 newObj.who(greeting, name)을 실행하는 함수가 리턴되는 클로저 함수가 되는 것이죠. 이 부분은 기존 생성자 함수 기본 패턴과 약간 차이가 있기 때문에, 여러 번 반복해서 읽어 이해하시기 바랍니다.

 


 

 

잠깐, 헷갈릴 수 있는 부분이 있어서 다시 한 번 짚고 넘어갈 코드가 있습니다.

 

function plus(x, y) {
    console.log(x + y);
}

var newT = function(a){
    return plus(a, "ON!");
}

newT(1);

// 출력 : 1ON!

 

어떤 함수의 실행 결과를 리턴하는 경우, 위와 같이 함수 실행문 자체를 리턴한다고 가정하면, 즉시 결과 값을 얻을 수 있습니다. 위의 saySomething에서도 비슷한 코드가 등장하니, 주의해 주세요.

 


 

추가로, newObj에서 파라미터 인자값으로 사용되는 this에 많은 혼란이 있을 수 있습니다. 생성자 함수 형태로 사용할 때, this.name = name....과 같은 형태로 this가 생성 객체와 바인딩 되는 경우로 학습했을 것이기 때문에 혼선이 올 수 있습니다.

 

아래와 같이, 생성자 함수 형태로 실행되는 경우에 this가 생성 객체와 바인딩 되지 않는 경우를 참고해주세요. 그러면 this가 newObj 자체가 바인딩 되는 것을 이해할 수 있을 것입니다. 

 

function testObj(){
    console.dir(this);
}
var test = new testObj();

function Person(name){
    console.dir(this);
    this.name = name;
    console.dir(this);
}

var personTest = new Person('JOHN');

// 출력
// testObj {}
// Person {}
// Person { name: 'JOHN' }