Entrambi gli esempi di codice che hai dimostrato nella tua domanda utilizzano l'ereditarietà prototipale. In effetti, qualsiasi codice orientato agli oggetti che scrivi in JavaScript è un paradigma di ereditarietà prototipale. JavaScript semplicemente non ha l'ereditarietà classica. Questo dovrebbe chiarire un po 'le cose:
Inheritance
|
+-----------------------------+
| |
v v
Prototypal Classical
|
+------------------------------+
| |
v v
Prototypal Pattern Constructor Pattern
Come puoi vedere l'ereditarietà prototipale e l'ereditarietà classica sono due diversi paradigmi di eredità. Alcuni linguaggi come Self, Lua e JavaScript supportano l'ereditarietà prototipale. Tuttavia, la maggior parte dei linguaggi come C ++, Java e C # supportano l'ereditarietà classica.
Una rapida panoramica della programmazione orientata agli oggetti
Sia l'ereditarietà prototipale che l'ereditarietà classica sono paradigmi di programmazione orientata agli oggetti (cioè si occupano di oggetti). Gli oggetti sono semplicemente astrazioni che incapsulano le proprietà di un'entità del mondo reale (cioè rappresentano le parole reali nel programma). Questo è noto come astrazione.
Astrazione: la rappresentazione delle cose del mondo reale nei programmi per computer.
Teoricamente un'astrazione è definita come "un concetto generale formato estraendo caratteristiche comuni da esempi specifici". Tuttavia, per il bene di questa spiegazione, utilizzeremo invece la definizione di cui sopra.
Ora alcuni oggetti hanno molte cose in comune. Ad esempio una moto da fango e una Harley Davidson hanno molto in comune.
Una bici da fango:

Una Harley Davidson:

Una moto da fango e una Harley Davidson sono entrambe moto. Quindi una bici è una generalizzazione sia di una mud bike che di una Harley Davidson.
Bike
|
+---------------------------------+
| |
v v
Mud Bike Harley Davidson
Nell'esempio sopra la bici, la mud bike e la Harley Davidson sono tutte astrazioni. Tuttavia la bici è un'astrazione più generale della mud bike e dell'Harley Davidson (cioè sia la mud bike che la Harley Davidson sono tipi specifici di moto).
Generalizzazione: un'astrazione di un'astrazione più specifica.
Nella programmazione orientata agli oggetti creiamo oggetti (che sono astrazioni di entità del mondo reale) e usiamo classi o prototipi per creare generalizzazioni di questi oggetti. Le generalizzazioni vengono create tramite ereditarietà. Una bici è una generalizzazione di una bici da fango. Quindi le bici da fango ereditano dalle biciclette.
Programmazione classica orientata agli oggetti
Nella classica programmazione orientata agli oggetti abbiamo due tipi di astrazioni: classi e oggetti. Un oggetto, come accennato prima, è un'astrazione di un'entità del mondo reale. Una classe d'altra parte è un'astrazione di un oggetto o di un'altra classe (cioè è una generalizzazione). Ad esempio, considera:
+----------------------+----------------+---------------------------------------+
| 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. |
+----------------------+----------------+---------------------------------------+
Come puoi vedere nei linguaggi di programmazione orientati agli oggetti classici, gli oggetti sono solo astrazioni (cioè tutti gli oggetti hanno un livello di astrazione di 1) e le classi sono solo generalizzazioni (cioè tutte le classi hanno un livello di astrazione maggiore di 1).
Gli oggetti nei classici linguaggi di programmazione orientati agli oggetti possono essere creati solo istanziando le classi:
class Human {
// ...
}
class Man extends Human {
// ...
}
Man johnDoe = new Man();
In sintesi, nei linguaggi di programmazione classici orientati agli oggetti gli oggetti sono astrazioni di entità del mondo reale e le classi sono generalizzazioni (cioè astrazioni di oggetti o di altre classi).
Quindi, quando il livello di astrazione aumenta, le entità diventano più generali e quando il livello di astrazione diminuisce, le entità diventano più specifiche. In questo senso il livello di astrazione è analogo a una scala che va da entità più specifiche a entità più generali.
Programmazione prototipale orientata agli oggetti
I linguaggi di programmazione prototipali orientati agli oggetti sono molto più semplici dei classici linguaggi di programmazione orientati agli oggetti perché nella programmazione prototipale orientata agli oggetti abbiamo solo un tipo di astrazione (cioè gli oggetti). Ad esempio, considera:
+----------------------+----------------+---------------------------------------+
| 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. |
+----------------------+----------------+---------------------------------------+
Come puoi vedere nei linguaggi di programmazione orientati agli oggetti prototipali, gli oggetti sono astrazioni di entità del mondo reale (nel qual caso sono semplicemente chiamati oggetti) o di altri oggetti (nel qual caso sono chiamati prototipi di quegli oggetti che astraggono). Quindi un prototipo è una generalizzazione.
Gli oggetti in linguaggi di programmazione orientati agli oggetti prototipali possono essere creati ex-nihilo (cioè dal nulla) o da un altro oggetto (che diventa il prototipo dell'oggetto appena creato):
var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);
A mio modesto parere, i linguaggi di programmazione orientati agli oggetti prototipi sono più potenti dei linguaggi di programmazione orientati agli oggetti classici perché:
- C'è solo un tipo di astrazione.
- Le generalizzazioni sono semplicemente oggetti.
A questo punto devi aver capito la differenza tra eredità classica ed eredità prototipale. L'ereditarietà classica è limitata alle classi che ereditano da altre classi. Tuttavia, l'ereditarietà dei prototipi include non solo i prototipi che ereditano da altri prototipi, ma anche gli oggetti che ereditano dai prototipi.
Isomorfismo di classe prototipo
Avrai notato che i prototipi e le classi sono molto simili. È vero. Loro sono. In effetti sono così simili che puoi effettivamente utilizzare i prototipi per modellare le classi:
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;
}
}
Usando la CLASSfunzione sopra puoi creare prototipi che assomigliano a classi:
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));
Tuttavia non è vero il contrario (cioè non puoi usare le classi per modellare prototipi). Questo perché i prototipi sono oggetti ma le classi non sono oggetti. Sono un tipo di astrazione completamente diverso.
Conclusione
Riassumendo, abbiamo appreso che un'astrazione è un "concetto generale formato estraendo caratteristiche comuni da esempi specifici" e che la generalizzazione è "un'astrazione di un'astrazione più specifica" . Abbiamo anche appreso le differenze tra eredità prototipale e classica e come entrambe siano due facce della stessa moneta.
In una nota di separazione vorrei sottolineare che ci sono due modelli di ereditarietà prototipale: il modello prototipale e il modello costruttore. Il modello prototipale è il modello canonico dell'ereditarietà prototipale mentre il modello del costruttore viene utilizzato per rendere l'ereditarietà prototipale più simile all'eredità classica. Personalmente preferisco il modello prototipale.
PS Sono il ragazzo che ha scritto il post sul blog " Why Prototypal Inheritance Matters " e ha risposto alla domanda " Benefits of prototypal inheritance over classic? ". La mia risposta è la risposta accettata.