1. ホーム
  2. javascript

[解決済み】javascriptの古典的な継承とプロトタイプの継承の比較

2022-02-11 04:05:21

質問

古典的な継承とプロトタイプ継承の違いについて、いろいろ調べてみたのですが、よくわかりません。

このページでいくつかのことを学びましたが、まだコンセプトについて混乱しています。

古典的な継承

// 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);

これは古典的な継承と似ているのでしょうか?プロトタイプ継承とは何なのか、全く分からないのですが?古典的な継承とは何ですか?古典的な継承はなぜ悪いのですか?

これらをわかりやすく理解するために、簡単な例を教えてください。

ありがとうございます。

シバ

解決方法は?

ご質問のコードサンプルは、いずれもプロトタイプ継承を利用しています。実際、あなたがJavaScriptで書くオブジェクト指向のコードはすべてプロトタイプ継承のパラダイムです。JavaScriptには古典的な継承がないのです。これで少しはすっきりしたのではないでしょうか。

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

プロトタイプ継承と古典的継承は異なる継承のパラダイムであることがおわかりいただけたと思います。SelfやLua、JavaScriptのようにプロトタイピング継承をサポートする言語もあります。しかし、C++、Java、C#のようなほとんどの言語では、古典的な継承をサポートしています。


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

プロトタイプ継承と古典的継承は、どちらもオブジェクト指向のプログラミングパラダイムです(つまり、オブジェクトを扱います)。オブジェクトは、現実世界の実体の特性をカプセル化した抽象的なものです(つまり、プログラムの中で現実の言葉を表すものです)。これを「抽象化」という。

抽象化すること。 現実世界のものをコンピュータのプログラムで表現すること。

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

さて、物には多くの共通点を持つものがあります。例えば、泥んこ自転車とハーレーダビッドソンには多くの共通点があります。

泥んこバイク。

ハーレーダビッドソンです。

泥んこバイクもハーレーダビッドソンもバイクである。したがって、バイクは泥んこバイクとハーレーダビッドソンの両方を一般化したものである。

                   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.              |
+----------------------+----------------+---------------------------------------+

プロトタイプ型オブジェクト指向プログラミング言語では、オブジェクトは現実世界の実体(この場合、単にオブジェクトと呼ばれる)または他のオブジェクト(この場合、抽象化したオブジェクトのプロトタイプと呼ばれる)の抽象化であることがおわかりいただけると思います。つまり、プロトタイプは汎化である。

プロトタイプオブジェクト指向プログラミング言語におけるオブジェクトは、無から創り出すことも、別のオブジェクトから創り出すこともできる(新たに創り出すオブジェクトのプロトタイプになる)。

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つがあることを述べておきたい。プロトタイパルパターンはプロトタイパル継承の標準的なパターンであり、コンストラクタパターンはプロトタイパル継承をより古典的な継承に近づけるために使われるパターンである。個人的には、プロトタイパルパターンの方が好きです。