'생성자'에 해당되는 글 1건

  1. 2008/09/01 JavaScript에서의 생성자와 멤버 함수 (1)
Languages/JavaScript2008/09/01 16:02
JavaScript는 클래스라는 개념을 제공하지 않기 때문에, 객체를 만들때 클래스의 이름을 줄 수가 없습니다. 따라서 객체를 만들때 클래스의 이름이 아니라, '함수'의 이름을 주어야 합니다.[각주:1]

다음의 코드를 봅시다.

function Foo(name) {
    this.name = name;
}

var obj = new Foo("obj 1");

이 코드에서 new 키워드의 의미는, 객체를 하나 만들되 그 객체에 대한 생성자로 그 다음에 오는 함수 이름을 사용하라는 뜻입니다. 따라서 함수 Foo는 생성자처럼 쓰일 수도 있고, 일반 함수처럼 쓰일 수도 있습니다. 다만 일반 함수로 사용하게 되면 위의 경우 this가 컨텍스트 오브젝트를 가리키게 되므로 좀 곤란하겠죠.

따라서 위와 같이 실행하고 obj.constructor == Foo 인지를 검사해보면 true가 됩니다. constructor 프로퍼티는 해당 변수(즉, 객체)가 만들어질 때 초기화를 담당한 함수를 참조합니다.

그럼 생성자는 그렇다 치고, 객체의 멤버 함수는 어떻게 정의해야 하나요? 앞서 function도 '객체'라고 했음을 상기합시다. 모든 function 객체는, prototype이라는 프라퍼티를 가집니다. 이 프라퍼티에 속한 프라퍼티는 해당 함수를 생성자로 사용해 만들어진 모든 객체에 자동적으로 추가됩니다. JavaScript에서는 어떤 프라퍼티에 하위 프라퍼티들을 동적으로 아무때나 추가할 수 있기 때문에, prototype 프라퍼티를 사용하면 멤버 함수들을 손쉽게 추가할 수 있습니다.

function Foo() {
   this.name = "baby";
   this.value = "cry";
}

Foo.prototype.getName() = funciton() {
    return this.name;
};

Foo.prortotype.getValue() = function() {
    return this.value;
};

var obj = new Foo();

alert( obj.getName() );
alert( obj.getValue() );

사실 여기까지만 하면 이해도 그다지 어렵지 않은 편입니다만... ㅎㅎ 더글러스 크록포드라는 몹쓸(?) 양반이 써놓은 기사를 읽어보면, 문제가 좀 더 복잡해집니다. http://javascript.crockford.com/private.html 이 기사는 JavaScript 객체에서 private 멤버 변수와 private 메소드를 어떻게 정의하고 사용할 수 있는지를 다루고 있습니다.

위의 글에 적힌 내용을 이해하려면, JavaScript에서의 유효범위(scope)의 개념을 숙고해 볼 필요가 있습니다. JavaScript에서 유효범위는 함수 내부에서 선언되었느냐, 아니면 함수 밖에서 선언되었느냐로 갈립니다. '선언'의 기준은 var 키워드가 사용되었느냐 사용되지 않았느냐로 갈리구요.

가령 다음과 같이 선언된 변수가 있다고 합시다.

var foo = 3;

함수 안에서 선언되지 않았으므로, 전역 변수입니다. 다음의 경우는 어떤가요?

function Foo() {
   var bar = 3;
}

함수 안에서 선언되었으므로 bar는 지역 변수입니다. 키워드 var가 사용되지 않았다면 전역 변수가 되었을 겁니다. 그럼 이 지역 변수는 Foo() 안에서만 사용 가능한 변수가 되겠군요. 그런데, 아마 다들 아시겠습니다만 JavaScript에는 클로저(closure)라는 개념이 있습니다.

What this means is that an inner function always has access to the vars and parameters of its outer function, even after the outer function has returned.

이 클로저라는 개념 덕에, 함수의 지역 변수를 마치 private 객체 변수인 것 처럼 사용할 수 있습니다. 함수 안에서 정의되는 함수는 외부 함수가 종료되더라도 외부 함수 안에서 정의된 변수나 외부 함수에 전달된 인자들을 그대로 사용할 수 있기 때문입니다.

크록포드 선생님이 작성한 다음 예제를 한번 보시죠.

function Container(param) {

    function dec() {
        if (secret > 0) {
            secret -= 1;
            return true;
        } else {
            return false;
        }
    }

    this.member = param;
    var secret = 3;
    var that = this;

    this.service = function () {
        if (dec()) {
            return that.member;
        } else {
            return null;
        }
    };
}
desc라는 함수를 안쪽에 하나 정의했습니다. 이 함수의 유효 범위는 역시 Container() 안으로 제한됩니다. 그러므로 외부에서는 사용할 수 없는 private 함수가 됩니다. 이 함수는 secret이라는 지역 변수를 사용하도록 구현되어 있습니다. desc()는 함수 안에서 정의된 함수이므로, 클로저가 갖는 성질을 고스란히 갖습니다. 따라서 이 함수는 Container()가 종료된 뒤에도 그 함수가 사용하던 지역 변수들을 그대로 사용할 수 있습니다. '클로저를 통해 지역 변수의 lifetime을 연장하여 private 변수로 기능하도록 만들었다'고 이해하면 되겠습니다.

그런데 desc는 그 유효범위가 Container() 안이기 떄문에, 다른 함수에서 이용하려면 public scope의 또다른 함수를 Container 안에 정의해 주어야 합니다. 위의 예제에서는 this.service에 할당한 function 객체가 그에 해당합니다. 클로저를 통해 private 변수와 메소드를 정의하고, 역시 또 클로저를 통해 그 메소드를 호출하는 릴레이 함수를 정의해주는 셈입니다. 새로 정의된 이 릴레이 함수 덕에, desc()의 lifetime 또한 연장될 수 있습니다. 크록포드는 이런 릴레이 함수를 privileged 메소드라고 부르고 있습니다. 이 메소드는 다음과 같이 호출하면 됩니다.

myContainer.service()

이렇게 호출하면 처음 세 번까지는 'abc'가 반환되고, 그 다음부터는 null이 반환됩니다. 한가지 알아두면 좋은 것은, privileged 메소드는 일반 public method와 달리 컴파일 시점이 아닌 실행 시간에 객체에 추가된다는 점입니다.

http://en.wikipedia.org/wiki/Closure_(computer_science)

Closure에 대한 설명은 위의 링크에서도 얻을 수 있습니다.


  1. JavaScript에서 함수 또한 객체라는 점을 이야기했었습니다. 이는 다음과 같이 함수를 정의하면 그 순간에 해당 함수에 대한 객체가 만들어짐을 의미합니다.

    function foo() {
        ...
    }

    그렇기 때문에, 함수를 정의하는 즉시 실행을 해 버릴 수도 있습니다. -_-;

    (function foo() {
       ...
    })();
    [본문으로]

'Languages > JavaScript' 카테고리의 다른 글

프로그래밍 jQuery (jQuery in Action)  (2) 2008/10/02
JavaScript에서의 생성자와 멤버 함수  (1) 2008/09/01
JavaScript에서의 function()  (0) 2008/09/01


Posted by 이병준

TRACKBACK http://www.buggymind.com/trackback/156 관련글 쓰기

댓글을 달아 주세요

  1. 설명이 정말 잘되있네요..^^ 감사합니다.

    2009/08/25 15:37 [ ADDR : EDIT/ DEL : REPLY ]