4

申し訳ありませんが、質問をより正確に指定する方法が本当にわかりません。継承の最も一般的な方法は、次の例で確認できます。

function Shape() {
  this.x = 0;
  this.y = 0;
}

//superclass method
Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
    console.info("Shape moved.");
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); //call super constructor.
}

//subclass extends superclass
Rectangle.prototype = new Shape();

var rect = new Rectangle();

rect.move(2, 1);

次のコードが気になります。

//subclass extends superclass
Rectangle.prototype = new Shape();

親プロトタイプのプロパティに直接接続した方がよいのではないでしょうか。

Rectangle.prototype = Shape.prototype;

私が理解している限り、これは Object から直接継承するすべてのオブジェクトに当てはまります。それらのプロトタイプは、オブジェクトのインスタンスではなく、そのプロトタイプ プロパティに接続されていますか?

上記の場合、 Shape.call(this); プロパティ(またはこれに似たもの)をコピーし、Shape.prototypeでそうであるメソッドのみを継承する必要があります。または、何かが足りないのでしょうか?

4

2 に答える 2

4

次のコードが気になります。

//subclass extends superclass
Rectangle.prototype = new Shape();

これは一般的なパターンですが、プロトタイプの作成インスタンスの初期化Shape の両方を使用しているため、あまり良いパターンではありません。どちらかを行う必要があります。(たとえば、引数を与える必要がある場合はどうするか考えてみてください。どのような引数を使用して を作成しますか?)ShapeRectangle.prototype

しかし、どちらも必要ありませんRectangle.prototype = Shape.prototype。なぜなら、物事を追加することができないからですRectangle.prototype(同じオブジェクトを指しているので、それらをインスタンスにもShape.prototype追加します)。Shape

したがって、doを介して作成されたオブジェクトと同じようにShape.prototype、プロトタイプとして を使用する (たとえば、 を継承する)新しいオブジェクトを作成する必要がありますが、作成するための呼び出しは必要ありませ。そして、その新しいオブジェクトを として使用します。Shape.prototypenew ShapeShapeRectangle.prototype

ECMAScript5 の時点で、次を使用してそれを行うことができますObject.create

Rectangle.prototype = Object.create(Shape.prototype);

Object.create新しいオブジェクトを作成し、最初の引数をオブジェクトの基になるプロトタイプとして割り当てます。

厳密には必要ではありませんが、それを行った後、constructor新しいオブジェクトにプロパティを設定して、次を参照するようにする必要がありRectangleます。

Rectangle.prototype.constructor = Rectangle;

そのようにするのは、 と の関係が、仕様の §13.2 で示されRectangle.prototypeているようにRectangle見えるからです。基本的に、コンストラクター関数がある場合は、を参照する必要があります。クローン作成操作などでこれに依存することがあります (JavaScript 自体はそれに依存していませ。たとえば、依存していません)。XX.prototype.constructorXinstanceof

したがって、ES5 を使用Object.createしてこれを行うことができます。しかし、今日でも、すべての JavaScript エンジンが をサポートしているわけではありませんObject.create。代わりに、一時的なコンストラクター関数を作成し、それをプロパティとして借用Shape.prototypeさせ、その一時的なコンストラクターを使用して as を使用する新しいオブジェクトを作成することにより、これを間接的に行うことができます。prototypeRectangle.prototype

function Ctor() { }
Ctor.prototype = Shape.prototype; // Borrow the prototype
Rectangle.prototype = new Ctor(); // Create the new object

...また、constructorプロパティを設定する必要があります。

Rectangle.prototype.constructor = Rectangle;

通常、毎回すべてを書き出すのではなく、次のようなヘルパーを使用します。

function derive(Parent, Child) {
    function Ctor() { this.constructor = Child; }
    Ctor.prototype = Parent.prototype;
    Child.prototype = new Ctor();
    return Child; // For chaining
}

次に、次のように使用します。

derive(Shape, Rectangle);

これらすべての最終結果は、インスタンスを初期化するためにShape内から呼び出すだけです。Rectanglecreate とは呼びませんRectangle.prototype

LineageJavaScript での継承の配管に興味がある場合は、上記を処理するスクリプトに興味があるかもしれません。私が「興味がある」と言うとき、必ずしもそれを使用することを意味するわけではありませんが (大歓迎ですが)、縮小されていないソースとこのページLineageを見て、ヘルパーなしで同じことを行うことと比較すると、これがどのように行われるかがわかります。ものは動作します。

于 2013-11-05T14:07:35.813 に答える
1

JavaScript は、プロトタイプのオブジェクト指向プログラミング言語です。これは、オブジェクトが他のオブジェクトから継承するということです。

JavaScript のオブジェクトには[[proto]]、そのオブジェクトのプロトタイプを指す特別なプロパティがあります。このプロパティは JavaScript エンジンによって内部的に維持され、各オブジェクトにはリンクされたプロトタイプのチェーンがあります。例えば:

          null
           ^
           | [[proto]]
           |
  +------------------+
  | Object.prototype |
  +------------------+
           ^
           | [[proto]]
           |
  +------------------+
  |  Shape.prototype |
  +------------------+
           ^
           | [[proto]]
           |
+---------------------+
| Rectangle.prototype |
+---------------------+
           ^
           | [[proto]]
           |
        +------+
        | rect |
        +------+

Rectangle.prototype = new Shape();プロトタイプ チェーンを作成すると、上図のようになります。これを行う新しい方法はRectangle.prototype = Object.create(Shape.prototype);.

          null
           ^
           | [[proto]]
           |
  +------------------+
  | Object.prototype |
  +------------------+
           ^
           | [[proto]]
           |
+---------------------+
| Rectangle.prototype |
|  / Shape.prototype  |
+---------------------+
           ^
           | [[proto]]
           |
        +------+
        | rect |
        +------+

書くRectangle.prototype = Shape.prototype;と、プロトタイプ チェーンは上記のようになります。これは、 2 つではなく 1 つのプロトタイプからのみ継承することRectangle.prototypeと同じです。これは、特定のメソッドを にShape.prototype追加できないことを意味します。RectangleRectangle.prototype

どの方法を選択する必要がありますか? 2 つの別個のプロトタイプがあるため、最初の方法を使用することを好みます。Rectangle特定のメソッドを に追加できますRectangle.prototype。また、実際にプロトタイプを拡張しています。2 番目のケースでは、継承はまったくありません。

これは、最近のコードの通常の書き方です。

function defclass(type, body) {
    var neo = function () {
        var self = new constructor;
        return prototype.hasOwnProperty("constructor") &&
            prototype.constructor.apply(self, arguments), self;
    };

    var constructor = function () {}; constructor.prototype = type;
    var prototype = constructor.prototype = new constructor; prototype.new = neo;
    return body.call(prototype, type), prototype;
}

を使用しdefclassて、次のようにクラスを作成できるようになりました。

var shape = defclass(null, function () {
    this.constructor = function () {
        this.x = 0;
        this.y = 0;
    };

    this.move = function (x, y) {
        this.x += x;
        this.y += y;
        console.info("Shape moved.");
    };
});

var rectangle = defclass(shape, function (shape) {
    this.constructor = function () {
        shape.constructor.call(this);
    };
});

最後に、次のようにクラスのインスタンスを作成できます。

var rect = rectangle.new();

rect.move(2, 1);

デモをご覧ください: http://jsfiddle.net/VHfBd/1

リンク:

  1. JavaScript でのオブジェクトの継承
  2. クラス宣言で擬似古典的な継承を実現する方法は?
  3. プロトタイプの継承が重要な理由
于 2013-11-05T14:38:10.280 に答える