web-dev-qa-db-ja.com

JavaScriptでの古典的な継承とプロトタイプの継承

私は非常に多くのリンクをグーグルで検索しましたが、古典的な継承とプロトタイプの継承の違いについてよく理解できませんか?

これらからいくつかのことを学びましたが、私はまだ概念について混乱しています。

古典的な継承

// Shape - superclass
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 = Object.create(Shape.prototype);

古典的継承は内部でプロトタイプ継承を使用しますか?

http://aaditmshah.github.io/why-prototypal-inheritance-matters/

上記のリンクから、古典的な継承では実行時に新しいメソッドを追加できないことを学びました。これは正しいです?ただし、上記のコードを確認できます。「move」メソッドと、実行時にプロトタイプを使用してメソッドを追加できます。これはプロトタイプベースの古典的な継承ですか?もしそうなら、実際の古典的な継承とプロトタイプの継承は何ですか?私はそれについて混乱しています。

プロトタイプの継承

function Circle(radius) {
    this.radius = radius;
}
Circle.prototype.area = function () {
    var radius = this.radius;
    return Math.PI * radius * radius;
};
Circle.prototype.circumference: function () {
    return 2 * Math.PI * this.radius;
};
var circle = new Circle(5);
var circle2 = new Circle(10);

これは古典的な継承に似ていますか?プロトタイプの継承とは何ですか?古典的な継承とは何ですか?なぜ古典的な継承が悪いのですか?

これらを簡単に理解するための簡単な例を教えてください。

おかげで、

シヴァ

108
SivaRajini

質問で示した両方のコードサンプルは、プロトタイプ継承を利用しています。実際、JavaScriptで記述するオブジェクト指向コードは、プロトタイプ継承のパラダイムです。 JavaScriptには古典的な継承がありません。これは少し物事をクリアする必要があります:

                                   Inheritance
                                        |
                         +-----------------------------+
                         |                             |
                         v                             v
                    Prototypal                     Classical
                         |
         +------------------------------+
         |                              |
         v                              v
Prototypal Pattern             Constructor Pattern

ご覧のとおり、プロトタイプの継承と古典的な継承は、継承の2つの異なるパラダイムです。 Self、Lua、JavaScriptなどの一部の言語は、プロトタイプ継承をサポートしています。ただし、C++、Java、C#などのほとんどの言語は、古典的な継承をサポートしています。


オブジェクト指向プログラミングの概要

プロトタイプの継承と古典的な継承の両方は、オブジェクト指向のプログラミングパラダイムです(つまり、オブジェクトを処理します)。オブジェクトは、現実世界のエンティティのプロパティをカプセル化する単純な抽象化です(つまり、プログラム内の実際のWordのことを表します)。これは抽象化として知られています。

Abstraction:コンピュータープログラムでの現実世界のものの表現。

理論的には、抽象化は「特定の例から共通の特徴を抽出することにより形成される一般的な概念」として定義されます。ただし、この説明のために、代わりに前述の定義を使用します。

現在、いくつかのオブジェクトには多くの共通点があります。たとえば、マッドバイクとハーレーダビッドソンには多くの共通点があります。

泥バイク:

A mud bike.

ハーレーダビッドソン:

A Harley Davidson

泥のバイクとハーレーダビッドソンはどちらもバイクです。したがって、自転車は泥自転車とハーレーダビッドソンの両方の一般化です。

                   Bike
                     |
    +---------------------------------+
    |                                 |
    v                                 v
Mud Bike                       Harley Davidson

上記の例では、自転車、泥自転車、およびハーレーダビッドソンはすべて抽象化されています。ただし、自転車は、泥自転車とハーレーダビッドソンのより一般的な抽象概念です(つまり、泥自転車とハーレーダビッドソンの両方が特定の種類の自転車です)。

一般化:より具体的な抽象化の抽象化。

オブジェクト指向プログラミングでは、オブジェクト(実世界のエンティティの抽象化)を作成し、クラスまたはプロトタイプを使用してこれらのオブジェクトの一般化を作成します。汎化は継承を介して作成されます。自転車は泥自転車の一般化です。したがって、泥自転車は自転車から継承されます。


古典的なオブジェクト指向プログラミング

古典的なオブジェクト指向プログラミングでは、クラスとオブジェクトという2種類の抽象化があります。前述のように、オブジェクトは実世界のエンティティの抽象化です。一方、クラスはオブジェクトまたは別のクラスの抽象化です(つまり、一般化です)。たとえば、次のことを考慮してください。

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity |                Comments               |
+----------------------+----------------+---------------------------------------+
| 0                    | John Doe       | Real World Entity.                    |
| 1                    | johnDoe        | Variable holding object.              |
| 2                    | Man            | Class of object johnDoe.              |
| 3                    | Human          | Superclass of class Man.              |
+----------------------+----------------+---------------------------------------+

古典的なオブジェクト指向プログラミング言語で見ることができるように、オブジェクトは抽象化のみです(つまり、すべてのオブジェクトの抽象化レベルは1です)。クラスは一般化のみです(つまり、すべてのクラスの抽象化レベルは1以上です)。

古典的なオブジェクト指向プログラミング言語のオブジェクトは、クラスをインスタンス化することによってのみ作成できます。

class Human {
    // ...
}

class Man extends Human {
    // ...
}

Man johnDoe = new Man();

古典的なオブジェクト指向プログラミング言語の要約では、オブジェクトは実世界のエンティティの抽象化であり、クラスは一般化(つまり、オブジェクトまたは他のクラスの抽象化)です。

したがって、抽象化のレベルが上がるとエンティティはより一般的になり、抽象化のレベルが下がるとエンティティはより具体的になります。この意味で、抽象化のレベルは、より具体的なエンティティからより一般的なエンティティに及ぶスケールに類似しています。


プロトタイプのオブジェクト指向プログラミング

プロトタイプのオブジェクト指向プログラミング言語は、古典的なオブジェクト指向プログラミング言語よりもはるかに単純です。プロトタイプのオブジェクト指向プログラミングでは、抽象化(つまりオブジェクト)が1種類しかないためです。たとえば、次のことを考慮してください。

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity |                Comments               |
+----------------------+----------------+---------------------------------------+
| 0                    | John Doe       | Real World Entity.                    |
| 1                    | johnDoe        | Variable holding object.              |
| 2                    | man            | Prototype of object johnDoe.          |
| 3                    | human          | Prototype of object man.              |
+----------------------+----------------+---------------------------------------+

プロトタイプのオブジェクト指向プログラミング言語でわかるように、オブジェクトは、実世界のエンティティ(この場合は単にオブジェクトと呼ばれます)または他のオブジェクト(この場合、それらは抽象化するオブジェクトのプロトタイプと呼ばれます)の抽象化です。したがって、プロトタイプは一般化です。

プロトタイプのオブジェクト指向プログラミング言語のオブジェクトは、ex-nihilo(つまり、無から)または別のオブジェクト(新しく作成されたオブジェクトのプロトタイプになる)から作成できます。

var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);

私の謙虚な意見では、プロトタイプのオブジェクト指向プログラミング言語は、従来のオブジェクト指向プログラミング言語よりも強力です。

  1. 抽象化のタイプは1つだけです。
  2. 汎化は単なるオブジェクトです。

これまでに、古典的継承とプロトタイプ継承の違いを理解しているはずです。古典的な継承は、他のクラスから継承するクラスに制限されます。ただし、プロトタイプ継承には、他のプロトタイプから継承するプロトタイプだけでなく、プロトタイプから継承するオブジェクトも含まれます。


プロトタイプクラス同型

プロトタイプとクラスが非常に似ていることに気づいたに違いありません。それは本当だ。彼らです。実際、これらは非常に似ているため、プロトタイプを使用してクラスをモデル化できます。

function CLASS(base, body) {
    if (arguments.length < 2) body = base, base = Object.prototype;
    var prototype = Object.create(base, {new: {value: create}});
    return body.call(prototype, base), prototype;

    function create() {
        var self = Object.create(prototype);
        return prototype.hasOwnProperty("constructor") &&
            prototype.constructor.apply(self, arguments), self;
    }
}

上記のCLASS関数を使用すると、クラスのように見えるプロトタイプを作成できます。

var Human = CLASS(function () {
    var milliseconds = 1
      , seconds      = 1000 * milliseconds
      , minutes      = 60 * seconds
      , hours        = 60 * minutes
      , days         = 24 * hours
      , years        = 365.2425 * days;

    this.constructor = function (name, sex, dob) {
        this.name = name;
        this.sex = sex;
        this.dob = dob;
    };

    this.age = function () {
        return Math.floor((new Date - this.dob) / years);
    };
});

var Man = CLASS(Human, function (Human) {
    this.constructor = function (name, dob) {
        Human.constructor.call(this, name, "male", dob);
        if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
    };
});

var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));

ただし、その逆は当てはまりません(つまり、クラスを使用してプロトタイプをモデル化することはできません)。これは、プロトタイプはオブジェクトですが、クラスはオブジェクトではないためです。それらはまったく異なるタイプの抽象化です。


結論

要約すると、抽象化は「特定の例から共通の特徴を抽出することによって形成される一般的な概念」であり、一般化は」の抽象化であることがわかりましたより具体的な抽象化」。また、プロトタイプの継承と古典的な継承の違い、およびそれらの両方が同じコインの2つの面である方法についても学びました。

別れのメモで、プロトタイプ継承には、プロトタイプパターンとコンストラクターパターンという2つのパターンがあることに注意したいと思います。プロトタイプパターンは、プロトタイプ継承の標準的なパターンですが、コンストラクターパターンは、プロトタイプ継承を従来の継承のように見せるために使用されます。個人的には、プロトタイプパターンを好みます。

追伸私は、ブログ投稿「 なぜプロトタイプ継承の問題 」を書き、質問「 プロトタイプ継承の利点? 」に回答した人です。私の答えは受け入れられた答えです。

231
Aadit M Shah

継承に入る前に、2つのprimaryモデルを見て、javascriptでインスタンス(オブジェクト)を作成します。

クラシックモデル:オブジェクトはブループリント(クラス)から作成されます

class Person {
  fn() {...}
} // or constructor function say, function Person() {}

// create instance
let person = new Person();

プロトタイプモデル:オブジェクトは別のオブジェクトから直接作成されます。

// base object
let Person = { fn(){...} }

// instance
let person = Object.create(Person);

どちらの場合も、Inheritance *は、プロトタイプオブジェクトを使用してオブジェクトをリンクすることによって実現されます。

(*基本クラスメソッドは、プロトタイプオブジェクトを通じて派生クラスからアクセスでき、派生クラスに明示的に存在する必要はありません。)

ここに、よりよく理解するための良い説明があります( http://www.objectplayground.com/

6
everlasto