klassische Vererbung vs prototypische Vererbung in Javascript

119

Ich habe so viele Links gegoogelt und kann keine gute Vorstellung vom Unterschied zwischen klassischer Vererbung und prototypischer Vererbung bekommen?

Ich habe einige Dinge daraus gelernt, bin aber immer noch verwirrt über die Konzepte.

Klassische Vererbung

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

Verwendet die klassische Vererbung die prototypische Vererbung im Inneren?

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

Durch den obigen Link habe ich erfahren, dass wir zur Laufzeit keine neuen Methoden für die klassische Vererbung hinzufügen können . Ist das richtig? Sie können jedoch den obigen Code überprüfen. Ich kann die "move" -Methode und alle Methoden zur Laufzeit über den Prototyp hinzufügen . Das ist also eine prototypbasierte klassische Vererbung? Wenn ja, was ist die tatsächliche klassische Vererbung und die Prototypvererbung? Das verwirrt mich.

Prototypische Vererbung.

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

Ist das ähnlich wie bei der klassischen Vererbung? Ich bin total verwirrt darüber, was prototypische Vererbung ist. Was ist klassisches Erbe? Warum ist klassisches Erbe schlecht?

Können Sie mir ein einfaches Beispiel geben, um diese auf einfache Weise besser zu verstehen?

Vielen Dank,

Siva

SivaRajini
quelle
Duplizieren Sie, überprüfen Sie dies heraus: stackoverflow.com/questions/1595611/…
Silviu Burcea
5
Ich bin mir nicht sicher, worum es hier geht - der erste Codeblock ist die prototypische Vererbung, nicht die klassische. Ihr zweiter Codeblock hat überhaupt keine Vererbung!
Alnitak
Dies kann xplain: blog.stephenwyattbush.com/2012/05/01/…
HasanAboShally
@alnitak developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Dieser Link zeigt an, dass es sich um eine klassische Vererbung handelt. deshalb bin ich verwirrt.
SivaRajini
Weitere Informationen darüber, warum Sie die klassische Vererbung vermeiden möchten, finden Sie in meinem Vortrag "Klassische Vererbung ist veraltet: Wie man in prototypischem OO denkt" vimeo.com/69255635
Eric Elliott

Antworten:

249

Beide Codebeispiele, die Sie in Ihrer Frage demonstriert haben, verwenden die prototypische Vererbung. Tatsächlich ist jeder objektorientierte Code, den Sie in JavaScript schreiben, ein Paradigma der prototypischen Vererbung. JavaScript hat einfach keine klassische Vererbung. Dies sollte die Dinge ein wenig aufklären:

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

Wie Sie sehen können, sind prototypische Vererbung und klassische Vererbung zwei verschiedene Paradigmen der Vererbung. Einige Sprachen wie Self, Lua und JavaScript unterstützen die prototypische Vererbung. Die meisten Sprachen wie C ++, Java und C # unterstützen jedoch die klassische Vererbung.


Ein kurzer Überblick über die objektorientierte Programmierung

Sowohl die prototypische Vererbung als auch die klassische Vererbung sind objektorientierte Programmierparadigmen (dh sie befassen sich mit Objekten). Objekte sind einfach Abstraktionen, die die Eigenschaften einer realen Entität einschließen (dh sie repräsentieren reale Wortsachen im Programm). Dies ist als Abstraktion bekannt.

Abstraktion: Die Darstellung realer Dinge in Computerprogrammen.

Theoretisch wird eine Abstraktion definiert als "ein allgemeines Konzept, das durch Extrahieren gemeinsamer Merkmale aus bestimmten Beispielen gebildet wird". Für diese Erklärung werden wir jedoch stattdessen die oben genannte Definition verwenden.

Jetzt haben einige Objekte viele Gemeinsamkeiten. Zum Beispiel haben ein Schlammrad und eine Harley Davidson viel gemeinsam.

Ein Schlammrad:

Ein Schlammrad.

Eine Harley Davidson:

Eine Harley Davidson

Ein Schlammrad und eine Harley Davidson sind beide Fahrräder. Daher ist ein Fahrrad eine Verallgemeinerung sowohl eines Schlammrads als auch einer Harley Davidson.

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

Im obigen Beispiel sind das Fahrrad, das Schlammrad und die Harley Davidson Abstraktionen. Das Fahrrad ist jedoch eine allgemeinere Abstraktion des Schlammfahrrads und der Harley Davidson (dh sowohl das Schlammfahrrad als auch die Harley Davidson sind bestimmte Arten von Fahrrädern).

Generalisierung: Eine Abstraktion einer spezifischeren Abstraktion.

In der objektorientierten Programmierung erstellen wir Objekte (die Abstraktionen realer Entitäten sind) und verwenden entweder Klassen oder Prototypen, um Verallgemeinerungen dieser Objekte zu erstellen. Verallgemeinerungen werden durch Vererbung erstellt. Ein Fahrrad ist eine Verallgemeinerung eines Schlammfahrrads. Daher erben Schlammfahrräder von Fahrrädern.


Klassische objektorientierte Programmierung

In der klassischen objektorientierten Programmierung gibt es zwei Arten von Abstraktionen: Klassen und Objekte. Ein Objekt ist, wie bereits erwähnt, eine Abstraktion einer realen Entität. Eine Klasse hingegen ist eine Abstraktion eines Objekts oder einer anderen Klasse (dh eine Verallgemeinerung). Betrachten Sie zum Beispiel:

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

Wie Sie in klassischen objektorientierten Programmiersprachen sehen können, sind Objekte nur Abstraktionen (dh alle Objekte haben eine Abstraktionsstufe von 1) und Klassen sind nur Verallgemeinerungen (dh alle Klassen haben eine Abstraktionsstufe größer als 1).

Objekte in klassischen objektorientierten Programmiersprachen können nur durch Instanziieren von Klassen erstellt werden:

class Human {
    // ...
}

class Man extends Human {
    // ...
}

Man johnDoe = new Man();

In klassischen objektorientierten Programmiersprachen sind Objekte Abstraktionen realer Entitäten und Klassen Verallgemeinerungen (dh Abstraktionen von Objekten oder anderen Klassen).

Daher werden Entitäten mit zunehmender Abstraktionsebene allgemeiner und mit abnehmender Abstraktionsebene Entitäten spezifischer. In diesem Sinne ist die Abstraktionsebene analog zu einer Skala, die von spezifischeren Entitäten bis zu allgemeineren Entitäten reicht.


Prototypische objektorientierte Programmierung

Prototypische objektorientierte Programmiersprachen sind viel einfacher als klassische objektorientierte Programmiersprachen, da wir in der prototypischen objektorientierten Programmierung nur einen Abstraktionstyp (dh Objekte) haben. Betrachten Sie zum Beispiel:

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

Wie Sie in prototypischen objektorientierten Programmiersprachen sehen können, sind Objekte Abstraktionen von Entitäten der realen Welt (in diesem Fall werden sie einfach als Objekte bezeichnet) oder anderen Objekten (in diesem Fall werden sie als Prototypen der Objekte bezeichnet, die sie abstrahieren). Ein Prototyp ist also eine Verallgemeinerung.

Objekte in prototypischen objektorientierten Programmiersprachen können entweder ex-nihilo (dh aus dem Nichts) oder aus einem anderen Objekt (das zum Prototyp des neu erstellten Objekts wird) erstellt werden:

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

Meiner bescheidenen Meinung nach sind prototypische objektorientierte Programmiersprachen leistungsfähiger als klassische objektorientierte Programmiersprachen, weil:

  1. Es gibt nur eine Art von Abstraktion.
  2. Verallgemeinerungen sind einfach Objekte.

Inzwischen müssen Sie den Unterschied zwischen klassischer Vererbung und prototypischer Vererbung erkannt haben. Die klassische Vererbung ist auf Klassen beschränkt, die von anderen Klassen erben. Die prototypische Vererbung umfasst jedoch nicht nur Prototypen, die von anderen Prototypen erben, sondern auch Objekte, die von Prototypen erben.


Isomorphismus der Prototypklasse

Sie müssen bemerkt haben, dass Prototypen und Klassen sehr ähnlich sind. Das ist richtig. Sie sind. Tatsächlich sind sie sich so ähnlich, dass Sie tatsächlich Prototypen verwenden können, um Klassen zu modellieren:

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;
    }
}

Mit der obigen CLASSFunktion können Sie Prototypen erstellen, die wie Klassen aussehen:

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

Das Gegenteil ist jedoch nicht der Fall (dh Sie können keine Klassen zum Modellieren von Prototypen verwenden). Dies liegt daran, dass Prototypen Objekte sind, Klassen jedoch keine Objekte. Sie sind eine ganz andere Art der Abstraktion.


Fazit

Zusammenfassend haben wir gelernt, dass eine Abstraktion ein "allgemeines Konzept ist, das durch Extrahieren gemeinsamer Merkmale aus bestimmten Beispielen gebildet wird", und dass die Verallgemeinerung "eine Abstraktion einer spezifischeren Abstraktion" ist . Wir haben auch die Unterschiede zwischen prototypischer und klassischer Vererbung kennengelernt und erfahren, wie beide zwei Gesichter derselben Medaille sind.

Zum Abschied möchte ich bemerken, dass es zwei Muster der prototypischen Vererbung gibt: das prototypische Muster und das Konstruktormuster. Das prototypische Muster ist das kanonische Muster der prototypischen Vererbung, während das Konstruktormuster verwendet wird, um die prototypische Vererbung eher wie die klassische Vererbung aussehen zu lassen. Persönlich bevorzuge ich das prototypische Muster.

PS Ich bin der Typ, der den Blog-Beitrag " Why Prototypal Inheritance Matters " geschrieben und die Frage " Vorteile der prototypischen Vererbung gegenüber der klassischen? " Beantwortet hat . Meine Antwort ist die akzeptierte Antwort.

Aadit M Shah
quelle
2
Danke für deine wundervolle Antwort. Ich muss verstehen, wie das prototypische Muster besser mit dem Konstruktormuster verglichen werden kann.
SivaRajini
1
Ich habe eine vergleichende Kritik über Konstruktoren gegen Prototypen in meinem Blog geschrieben: aaditmshah.github.io/why-prototypal-inheritance-matters/…
Aadit M Shah
Wäre es also richtig zu sagen, dass wir, wenn wir Funktionen in Javascript verwenden, um eine Vererbung zu erreichen, das klassische Vererbungsmodell verwenden und wenn wir einfache Objekte verwenden, eine prototypische Vererbung (beide intern nach einer prototypischen Vererbung)?
Swanidhi
1
@Swanidhi Nein. Wenn Sie JavaScript verwenden, verwenden Sie das prototypische Vererbungsmodell. JavaScript bietet jedoch zwei Arten der prototypischen Vererbung: Verwendung von Funktionen (dh des Konstruktormusters) und Verwendung von Objekten (dh des prototypischen Musters).
Aadit M Shah
5
@Swanidhi Nein. Es ist immer noch prototypisch. JavaScript hat keine "Klassen" und daher absolut nichts in JavaScript in klassischer Sprache, einschließlich Konstruktoren. Es ist immer noch eine prototypische Vererbung. Nur eine seltsame Form der prototypischen Vererbung, die Menschen mit der klassischen Vererbung verwechseln. Kurz gesagt, programming with classes = classical inheritance, programming with prototypes = prototypal inheritance, programming with constructors = weird form of prototypal inheritance that looks a lot like classical inheritance. Hoffe das klärt die Dinge.
Aadit M Shah
8

Bevor wir uns mit der Vererbung befassen, werden wir uns zwei Hauptmodelle ansehen , um Instanzen (Objekte) in Javascript zu erstellen:

Klassisches Modell: Das Objekt wird aus einer Blaupause (Klasse) erstellt.

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

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

Prototypisches Modell: Das Objekt wird direkt aus einem anderen Objekt erstellt.

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

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

In beiden Fällen wird die Vererbung * durch Verknüpfen von Objekten mithilfe eines Prototypobjekts erreicht.

(* Basisklassenmethoden sind über die abgeleitete Klasse über das Prototypobjekt zugänglich und müssen in der abgeleiteten Klasse nicht explizit vorhanden sein.)

Hier ist eine gute Erklärung, um besser zu verstehen ( http://www.objectplayground.com/ )

Everlasto
quelle
0

Ein Hund ist ein Tier. Suzanna ist ein Hund. In der klassischen Vererbung Animalist eine Klasse, Dogist eine Unterklasse von Animalund suzannaist eine Instanz von a Dog.

Bei der prototypischen Vererbung gibt es keine Klasse. Sie haben eine animal, die ein Objekt ist. A dogist ein anderes Objekt, das klont und erweitert animal(das Prototypobjekt). suzannaist ein drittes Objekt, das kopiert und erweitert dog.

let animal = {hasChlorophyl: false};

let dog = Object.create(animal);
Object.assign(dog, {
  speak() {
    console.log("Woof!");
  }
});

let suzanna = Object.create(dog);
Object.assign(suzanna, {
  name: "Suzanna"
});

suzanna.speak();

Wenn Sie schreiben , Dogstatt dog, vor allem , wenn Sie machen Dogeine Art von „Konstruktor“ -Funktion, dann tun Sie nicht prototypische Vererbung; Sie machen (pseudo-) klassische Vererbung . Die Tatsache, dass Sie Object.create()dies verwenden, bedeutet nicht, dass Sie eine prototypische Vererbung durchführen.

Tatsächlich unterstützt JavaScript nur die prototypische Vererbung. Der verwirrende newOperator und das verwirrende .prototypeAttribut sind vorhanden, damit die prototypische Vererbung wie eine (pseudo-) klassische Vererbung aussieht.

Douglas Crockford untersucht dies ausführlich in seinem Buch "JavaScript: The Good Parts".

Antonis Christofides
quelle