Prototype inherits() 를 이용한 상속

황제낙엽 2012.07.18 11:38 조회 수 : 129

sitelink1 http://d.hatena.ne.jp/brazil/20051012/1129035427 
sitelink2  
sitelink3  
sitelink4 http://1 
extra_vars4 ko 
extra_vars5  
extra_vars6 sitelink1 

원문 : Classical Inheritance in JavaScript

저자 : Douglas Crockford

 

※ 원문의 코드에 잘못이있어, 이대로 작성하면 작동하지 않습니다. 저자 의견과 추가 정보는 번역 참고 를 참조하십시오.

 

 

 

 

너는 자신이 똑똑하고 계급에도 속하지 않고 자유 라니 믿고있는

John Lennon

JavaScript 는 클래스라는 개념에 얽매이지 않은 언어입니다. JavaScript 는 고전적인 상속 대신 프로 타입적인 상속 을 사용합니다. 이것은 C + + 과 Java 와 같은 기존의 객체 지향 언어 에 뛰어난 프로그래머 를 당황하게 할지도 모릅니다.JavaScript 의 프로토 타입 적인 상속 이 고전적인 상속 에 비해 얼마나 표현력이 우수 있는지, 앞으로 살펴보겠습니다.

 

Java JavaScript
강력한 타이핑 약한 타이핑
정적 동적
고전 프로토 타입 으로
클래스 함수
생성자 함수
메서드 함수

 

 

원래 우리는 왜 상속 을 걱정합니까? 그것은 주로 두 가지 이유가 있습니다. 첫 번째 이유는 형식에 대한 편의를위한 것입니다. 우리는 언어 시스템에 대해 유사한 클래스 참조에 자동 캐스팅할달라고하고 싶다. 개체 참조 캐스트, 정형적이고 명시적인 기술이 필요한 형식 시스템에서 형식 안전성을 거의 얻을 수 없기 때문입니다. 이것은 강한 타입 언어에서는 매우 중요하지만 JavaScript 와 같은 약한 타입 언어에서는 객체 참조 캐스트 필요가 없기 때문에 큰 의미가 없습니다.

 

 

두 번째 이유는 코드의 재사용입니다. 많은 개체 모두가 동일한 메서드를 구현하는 것은 좋지 있습니다. 클래스를 사용하는 것으로, 1 세트의 정의에서 그 모든 객체 를 생성할 수 있습니다. 또한 일부 메서드만 다르거나 많거나하는 유사한 개체가있을 수 많습니다. 이 장면에서 고전적인 상속 도 유용하지만 프로토 타입 적인 상속 은 더 편리합니다.

 

 

이것을 증명하기 위해 먼저 기존의 고전적인 언어와 비슷한 패턴으로 쓰기 위해 설탕 을 소개하고, 이어 고전적인 언어로 제공되지 않는 유용한 패턴을 살펴보겠습니다. 그리고 마지막으로, 설탕 에 대해 설명하려고합니다.

 

 

고전적인 상속

 

먼저 Parenizor 클래스를 만듭시다. Parenizor 클래스는, value 설정하고 검색하는 메서드와 value를 괄호로 묶어 toString 메소드를 가집니다.

 

function Parenizor (value) {
    this.setValue (value);
}

Parenizor.method ( 'setValue', function (value) {
    this.value = value;
    return this;
});

Parenizor.method ( 'getValue', function () {
    return this.value;
});

Parenizor.method ( 'toString', function () {
    return '('+ this.getValue () + ')';
});

 

이 구문은 조금 낯설지만 그 안에 쉽게 고전적인 패턴을 간파할 수있을 것이다. method 메서드는 메서드와 함수 를 가지고 가고 공용 메서드로 그들을 클래스에 추가합니다.

 

그럼, 조속히 이것을 사용해 보자.

 

myParenizor = new Parenizor (0);
myString = myParenizor.toString ();

기대대로, myString은 "(0)"입니다.

 

다음 Parenizor를 상속 하는 다른 클래스를 만듭시다.

 

이 클래스는, value 0이거나 비어있는 경우 toString 메소드가 "-0 -"를 반환 이외는 동일합니다.

 

function ZParenizor (value) {
    this.setValue (value);
}

ZParenizor.inherits (Parenizor);

ZParenizor.method ( 'toString', function () {
    if (this.getValue ()) {
        return this.uber ( 'toString');
    }
    return "-0 -";
});

 

inherits 메서드는 Java 의 extends와 비슷합니다. uber 메서드는 Java 의 super 유사한 방법 중에서 부모클래스의 메소드를 호출합니다. 예약어 의 한계를 해결하기 위해 메서드의 이름을 바꾸고 있습니다.)

 

결과를 확인합시다.

 

myZParenizor = new ZParenizor (0);
myString = myZParenizor.toString ();

이번은 myString이 "-0 -"있습니다.

 

JavaScript 클래스는 아니지만, 마치 클래스가 것처럼 프로그램 을 작성할 수도있는 것입니다.

 

 

다중 상속

 

함수 의 prototype 객체 를 조작하는 것으로, 여러 클래스의 메소드로부터 구성되는 클래스를 만들고 다중 상속 을 구현할 수 있습니다. 마구 다중 상속 을 이용하면 구현이 어려워 메소드 이름 충돌로 고통받는 처지가 될지도 모릅니다.JavaScript 는 판단력없이 다중 상속 을 사용할 수 있습니다. 그러나 다음 예제에서 스위스 상속 보다는 규율있는 방법을 사용하여 봅시다.

 

여기에서는 NumberValue라는 클래스를 생각 해보자. NumberValue는 value가 일정 범위의 숫자를 확인하고 필요에 따라 예외를 던질 setValue 메소드를 가집니다. 우리가 ZParenizor 필요한 것은 setValue와 setRange 방법뿐 toString 전혀 필요 없습니다. 에서는 이것을 써 보겠습니다.

 

ZParenizor.swiss (NumberValue 'setValue', 'setRange');

이 코드에서 필요한 메서드만 클래스에 추가합니다.

기생적인 상속

ZParenizor를 다른 방법으로 쓸 수 있습니다. Parenizor를 상속 하는 대신 Parenizor의 생성자 를 호출, 대역으로 Parenizor의 인스턴스 를 반환 생성자 를 만드는 것입니다. 이 생성자 는 공용 메서드 대신 프리빌리지 드 (권한) 메서드를 추가합니다.

 

function ZParenizor2 (value) {
    var self = new Parenizor (value);
    self.toString = function () {
        if (this.getValue ()) {
            return this.uber ( 'toString');
        }
        return "-0 -"
    };
    return self;
}

 

고전적인 상속 은 is-a (이것은 - 무엇 무엇) 관계입니다. 대해 기생적인 상속 은 was-a-but-now's-a (예전에는 - 삑삑 - 이었지만 - 지금은 - 무엇 무엇) 관계 같은 느낌입니다. 객체 의 구축 상황에서 생성자 가 중요한 역할을 담당하고 있습니다. 여전히 uber (구 super) 메소드는 프리빌리지 드 메소드에서 호출할 수있는 점에도 주목하십시오.

클래스 강화

JavaScript 의 다이너 미즘은 기존 클래스의 메소드를 추가하거나 바꿀 수 있습니다. method 메서드는 언제라도 호출할 수 현존하는 인스턴스 와 미래 인스턴스 의 모든 것이 새롭게 추가된 메소드를 가질 수 있습니다. 즉, 언제든지 문자 클래스를 확장 (extend) 수있다는 것입니다. 상속 은 소급 같이 작동합니다. 이 확장기구의 것을 다른 것을 의미한다 Java 확장 (extends)과 혼동을 피하기 위해 여기서는 클래스 강화 ( Class augmentation)라고합시다.

객체 강화

정적 객체 지향 언어 에서는 다른 개체 와 조금 다른 개체 가 필요한 경우 새 클래스를 정의해야했습니다. 그러나JavaScript 에서는 클래스를 추가하지 않고도 개별 객체 에 직접 메소드를 추가할 수 있습니다. 이것은 클래스의 수를 크게 줄여 더 간단한 코드를 쓸 수 있도록하기 때문에 매우 강력합니다. JavaScript 의 객체 가 해시 테이블에 비슷한 것을 기억하십시오. 언제든지 새 값을 개체 에 추가할 수 있습니다. 그리고 추가한 값이 함수 라면, 그것은 메서드가됩니다.

 

위의 예제에서는 ZParenizor 클래스는 전혀 필요 없습니다. 그리고 쉽게 인스턴스 를 수정할 수있었습니다.

 

 

myParenizor = new Parenizor (0);
myParenizor.toString = function () {
    if (this.getValue ()) {
        return this.uber ( 'toString');
    }
    return "-0 -";
};
myString = myParenizor.toString ();

 

상속 형식을 전혀 사용하지 않고 myParenizor 인스턴스 에 toString 메소드를 추가했습니다. JavaScript 는 클래스라는 개념에 얽매이지 않는 언어 때문에 우리는 개별 인스턴스 를 발전시킬 수있는 것입니다.

슈가

이전 예제에서, 나는 4 개의 슈가 메서드를 사용했습니다.

처음에는 method 메소드입니다. 이것은 클래스 인스턴스 메서드를 추가합니다.

Function.prototype.method = function (name, func) {
    this.prototype [name] = func;
    return this;
};

여기서는 Function. prototype 에 공용 메서드를 추가합니다. 따라서 모든 함수 에서 클래스 보강을 할 수 있습니다.method 메서드는 이름과 함수 를 인수 로 가지고 가고 함수 의 prototype 객체 에 추가합니다.

 

method 메서드는 this를 반환합니다. 난 보통 값을 돌려줄 필요가없는 메서드는 this를 반환합니다. 이것은 계단식 스타일 (세로줄 형식) 프로그래밍 이 가능하기 때문입니다.

 

 

다음은 inherits 방법입니다. 이것은 클래스가 다른 클래스를 상속 하는 것을 나타냅니다. inherits 메서드는부모와 자식 두 클래스를 정의한 후하고 자식 클래스에 메소드가 추가되기 전에 호출해야합니다.

 

Function.method ( 'inherits', function (parent) {
    var d = 0, p = (this.prototype = new parent ());
    this.method ( 'uber', function uber (name) {
        var f, r, t = d, v = parent.prototype;
        if (t) {
            while (t) {
                v = v.constructor.prototype;
                t - = 1;
            }
            f = v [name];
        } else {
            f = p [name];
            if (f == this [name]) {
                f = v [name];
            }
        }
        d + = 1;
        r = f.apply (this, Array.prototype.slice.apply (arguments [1]));
        d - = 1;
        return r;
    });
    return this;
});

다시 Function을 강화합니다. parent 클래스의 인스턴스 를 만들고 새로운 prototype 으로 그것을 사용합니다.constructor 필드를 수정하고 마찬가지로 prototype 에 uber 메서드를 추가합니다.

 

uber 메서드는 자신의 prototype 에서 해당 이름을 가진 메서드를 찾습니다. 발견 방법은 기생적인 상속 또는 개체 보강의 경우 호출 함수 입니다. 고전적인 상속 을하고있는 경우, parent의 prototype 중에서 함수 를 찾아야합니다.

 

return 문은 Function의 apply 메소드를 사용하여 함수 를 호출합니다. 명시적으로 this를 설정하고 인수 의 배열 을 전달합니다. 인수 (있는 경우)는 arguments 배열 에서 가져옵니다. 아쉬움에 arguments 배열 은 엄밀하게는 배열 이 아니기 때문에 배열 의 slice 메서드를 호출하기 위해 다시 apply를 사용하지 않으면 안됩니다.

 

최후는 swiss 방법입니다.

 

Function.method ( 'swiss', function (parent) {
    for (var i = 1; i <arguments.length; i + = 1) {
        var name = arguments [i];
        this.prototype [name] = parent.prototype [name];
    }
    return this;
});

swiss 메서드는 arguments을 반복하고 parent의 prototype 에서 새 클래스의 prototype 에 각 name과 일치하는 멤버를 복사합니다.

결론

고전적인 언어와 같이 JavaScript 를 사용할 수 있습니다. 하지만 JavaScript 는 탁월한 표현력 또한 가지고있는 것입니다. 우리는 고전적인 상속 , 스위스 상속 기생적인 상속 클래스 보강, 개체 강화와보고 왔습니다. Java 보다 작고 간단하다고 생각되는 언어에서 이러한 다양한 코드 재사용 패턴 집합을 가져온 것입니다.

 

고전적인 개체 는 단단한입니다. 단단한 개체 에 새 구성원을 추가하는 유일한 방법은 새로운 클래스를 만드는 것입니다.한편 JavaScript 의 개체 는 부드럽습니다. 단순한 대입 부드러운 객체 에 새로운 멤버를 추가할 수 있습니다.

 

 

매우 유연한 JavaScript 의 객체 를 알고, 당신은 클래스 계층에 대한 다른 생각을 갖고 싶어지는 것입니다. 깊은 계층은 잘못된 것입니다. 얕은 계층은 표현이 풍부 효율적입니다.

 

[출처] javascript 클레스 상속|작성자 오터넷