Obie próbki kodu zademonstrowane w Twoim pytaniu wykorzystują dziedziczenie prototypowe. W rzeczywistości każdy kod zorientowany obiektowo, który piszesz w JavaScript, jest paradygmatem dziedziczenia prototypowego. JavaScript po prostu nie ma klasycznego dziedziczenia. To powinno trochę wyjaśnić:
Inheritance
|
+-----------------------------+
| |
v v
Prototypal Classical
|
+------------------------------+
| |
v v
Prototypal Pattern Constructor Pattern
Jak widać dziedziczenie prototypowe i klasyczne to dwa różne paradygmaty dziedziczenia. Niektóre języki, takie jak Self, Lua i JavaScript, obsługują dziedziczenie prototypowe. Jednak większość języków, takich jak C ++, Java i C #, obsługuje klasyczne dziedziczenie.
Krótki przegląd programowania obiektowego
Dziedziczenie prototypowe i klasyczne są paradygmatami programowania obiektowego (tj. Dotyczą obiektów). Obiekty są po prostu abstrakcjami, które obejmują właściwości istoty świata rzeczywistego (tj. Reprezentują rzeczywiste rzeczy słowne w programie). Nazywa się to abstrakcją.
Abstrakcja: Reprezentacja rzeczy ze świata rzeczywistego w programach komputerowych.
Teoretycznie abstrakcję definiuje się jako „ogólną koncepcję utworzoną przez wyodrębnienie wspólnych cech z konkretnych przykładów”. Jednak ze względu na to wyjaśnienie zamiast tego użyjemy powyższej definicji.
Niektóre obiekty mają teraz wiele wspólnego. Na przykład rower błotny i Harley Davidson mają ze sobą wiele wspólnego.
Rower błotny:
Harley Davidson:
Rower błotny i Harley Davidson to rowery. Stąd rower jest uogólnieniem zarówno roweru błotnego, jak i Harleya Davidsona.
Bike
|
+---------------------------------+
| |
v v
Mud Bike Harley Davidson
W powyższym przykładzie rower, motocykl błotny i Harley Davidson są abstrakcjami. Jednak rower jest bardziej ogólną abstrakcją roweru błotnego i Harleya Davidsona (tj. Zarówno rower błotny, jak i Harley Davidson to specyficzne typy rowerów).
Uogólnienie: abstrakcja bardziej szczegółowej abstrakcji.
W programowaniu obiektowym tworzymy obiekty (które są abstrakcjami bytów ze świata rzeczywistego) i używamy klas lub prototypów do tworzenia uogólnień tych obiektów. Uogólnienia są tworzone przez dziedziczenie. Rower to uogólnienie roweru błotnego. Stąd rowery błotne dziedziczą po rowerach.
Klasyczne programowanie obiektowe
W klasycznym programowaniu obiektowym mamy dwa rodzaje abstrakcji: klasy i obiekty. Obiekt, jak wspomniano wcześniej, jest abstrakcją bytu świata rzeczywistego. Klasa z drugiej strony jest abstrakcją obiektu lub innej klasy (czyli jest uogólnieniem). Weźmy na przykład pod uwagę:
+----------------------+----------------+---------------------------------------+
| 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. |
+----------------------+----------------+---------------------------------------+
Jak widać w klasycznych zorientowanych obiektowo językach programowania obiekty są tylko abstrakcjami (tj. Wszystkie obiekty mają poziom abstrakcji 1), a klasy są tylko uogólnieniami (tj. Wszystkie klasy mają poziom abstrakcji większy niż 1).
Obiekty w klasycznych zorientowanych obiektowo językach programowania można tworzyć tylko poprzez tworzenie instancji klas:
class Human {
// ...
}
class Man extends Human {
// ...
}
Man johnDoe = new Man();
Podsumowując, w klasycznych zorientowanych obiektowo językach programowania obiekty są abstrakcjami bytów ze świata rzeczywistego, a klasy są uogólnieniami (tj. Abstrakcjami obiektów lub innych klas).
Stąd wraz ze wzrostem poziomu abstrakcji byty stają się bardziej ogólne, a wraz ze spadkiem poziomu abstrakcji, byty stają się bardziej specyficzne. W tym sensie poziom abstrakcji jest analogiczny do skali od bytów bardziej szczegółowych do bytów bardziej ogólnych.
Prototypowe programowanie obiektowe
Prototypowe zorientowane obiektowo języki programowania są znacznie prostsze niż klasyczne zorientowane obiektowo języki programowania, ponieważ w prototypowym programowaniu zorientowanym obiektowo mamy tylko jeden typ abstrakcji (tj. Obiekty). Weźmy na przykład pod uwagę:
+----------------------+----------------+---------------------------------------+
| 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. |
+----------------------+----------------+---------------------------------------+
Jak widać w prototypowych obiektowych językach programowania, obiekty są abstrakcjami albo bytów świata rzeczywistego (w takim przypadku są po prostu nazywane obiektami), albo innych obiektów (w takim przypadku nazywane są prototypami tych obiektów, które są abstrakcyjne). Stąd prototyp jest uogólnieniem.
Obiekty w prototypowych zorientowanych obiektowo językach programowania mogą być tworzone ex-nihilo (czyli z niczego) lub z innego obiektu (który staje się prototypem nowo utworzonego obiektu):
var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);
Moim skromnym zdaniem prototypowe zorientowane obiektowo języki programowania są potężniejsze niż klasyczne zorientowane obiektowo języki programowania, ponieważ:
- Jest tylko jeden rodzaj abstrakcji.
- Uogólnienia to po prostu przedmioty.
Do tej pory musieliście zdać sobie sprawę z różnicy między dziedziczeniem klasycznym a dziedziczeniem prototypowym. Dziedziczenie klasyczne jest ograniczone do klas dziedziczących po innych klasach. Jednak dziedziczenie prototypów obejmuje nie tylko prototypy dziedziczące po innych prototypach, ale także obiekty dziedziczące po prototypach.
Izomorfizm klasy prototypowej
Pewnie zauważyłeś, że prototypy i klasy są bardzo podobne. To prawda. Oni są. W rzeczywistości są tak podobne, że można faktycznie używać prototypów do modelowania klas:
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;
}
}
Korzystając z powyższej CLASS
funkcji możesz tworzyć prototypy, które wyglądają jak klasy:
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));
Jednak sytuacja odwrotna nie jest prawdą (tj. Nie można używać klas do modelowania prototypów). Dzieje się tak, ponieważ prototypy są obiektami, ale klasy nie są obiektami. To zupełnie inny rodzaj abstrakcji.
Wniosek
Podsumowując, dowiedzieliśmy się, że abstrakcja jest „ogólnym pojęciem utworzonym przez wyodrębnienie wspólnych cech z konkretnych przykładów”, a uogólnienie jest „abstrakcją bardziej szczegółowej abstrakcji” . Dowiedzieliśmy się również o różnicach między dziedziczeniem prototypowym i klasycznym oraz o tym, że oba są dwoma obliczami tej samej monety.
Na pożegnanie chciałbym zauważyć, że istnieją dwa wzorce dziedziczenia prototypowego: wzorzec prototypowy i wzorzec konstruktora. Wzorzec prototypowy to kanoniczny wzorzec dziedziczenia prototypowego, podczas gdy wzorzec konstruktora służy do nadania dziedziczeniu prototypowemu wyglądowi bardziej przypominającego dziedziczenie klasyczne. Osobiście wolę prototypowy wzór.
PS Jestem gościem, który napisał na blogu wpis „ Dlaczego dziedziczenie prototypowe ma znaczenie ” i odpowiedział na pytanie „ Korzyści z dziedziczenia prototypowego nad klasycznym? ”. Moja odpowiedź jest zaakceptowana.