In che modo l'eredità prototipale è praticamente diversa dall'eredità classica?


27

Ereditarietà, polimorfismo ed incapsulamento sono le tre caratteristiche più distinte e importanti di OOP, e da esse l'ereditarietà ha oggi statistiche di utilizzo elevato. Sto imparando JavaScript, e qui, tutti dicono che ha un'eredità prototipale, e le persone ovunque dicono che è qualcosa di molto diverso dall'eredità classica.

Tuttavia, non riesco a capire qual è la loro differenza dal punto di vista pratico? In altre parole, quando si definisce una classe base (prototipo) e quindi ne derivano alcune sottoclassi, entrambi si ha accesso alle funzionalità della propria classe base e si possono aumentare le funzioni sulle classi derivate. Se consideriamo quello che ho detto essere il risultato previsto dell'ereditarietà, allora perché dovremmo preoccuparci se stiamo usando la versione prototipo o classica?

Per chiarirmi di più, non vedo alcuna differenza nell'utilità e negli schemi di utilizzo dell'eredità prototipale e classica. Ciò non ha alcun interesse a scoprire perché sono diversi, poiché entrambi danno come risultato la stessa cosa, OOAD. In che modo l'eredità prototipale praticamente (non teoricamente) è diversa dall'eredità classica?

Risposte:


6

Post di blog recenti su JS OO

Credo che il tuo confronto sia l'emulazione OO classica in JavaScript e OO classica e ovviamente non puoi vedere alcuna differenza.

Dichiarazione di non responsabilità: sostituire tutti i riferimenti a "prototipo OO" con "prototipo OO in JavaScript". Non conosco i dettagli del Sé o di qualsiasi altra implementazione.

Tuttavia, il prototipo di OO è diverso. Con i prototipi hai solo oggetti e puoi iniettare oggetti solo nella catena di prototipi di altri oggetti. Ogni volta che si accede a una proprietà su un oggetto, si cerca quell'oggetto e tutti gli oggetti nella catena del prototipo.

Per OO prototipo non esiste una nozione di incapsulamento. L'incapsulamento è una caratteristica di ambito, chiusure e funzioni di prima classe, ma non ha nulla a che fare con il prototipo di OO. Inoltre, non esiste alcuna nozione di eredità, ciò che la gente chiama "eredità" è in realtà solo polimorfismo.

Tuttavia ha polimorfismo.

Per esempio

var Dog = {
  walk: function() { console.log("walks"); }
}

var d = Object.create(Dog);
d.walk();

Chiaramente dha accesso al metodo Dog.walke questo mostra polimorfismo.

Quindi in realtà c'è una grande differenza . Hai solo polimorfismo.

Tuttavia, come accennato, se lo desideri (non ho idea del perché lo faresti) puoi emulare OO classico in JavaScript e avere accesso all'incapsulamento e all'eredità (limitati).


1
@Rayons, Google per eredità prototipica e vedrai che arriva l' eredità prototipale . Anche da Yahoo!
Saeed Neamati,

L'eredità di Saeed è un termine vago e comunemente usato in modo improprio. Che cosa significano con "eredità", io etichetta "polimorfismo". Le definizioni sono troppo vaghe.
Raynos,

No @Rayons, intendevo che la parola prototipo è il termine corretto, non prototipico . Non ho parlato di eredità :)
Saeed Neamati,

1
@Saeed è uno di quegli errori di battitura che la mia mente cancella. Non distolgo l'attenzione su questo
Raynos l'

1
Hmm .. terminologia. ick. Chiamerei il modo in cui gli oggetti javascript sono correlati alla loro delega "delegazione". Il polimorfismo mi sembra una preoccupazione di livello inferiore, in base al fatto che un determinato nome può indicare un codice diverso per oggetti diversi.
Sean McMillan,

15

L'eredità classica eredita il comportamento, senza alcuno stato, dalla classe genitore. Eredita il comportamento nel momento in cui l'oggetto viene istanziato.

L'eredità prototipica eredita il comportamento e lo stato dall'oggetto genitore. Eredita il comportamento e lo stato nel momento in cui viene chiamato l'oggetto. Quando l'oggetto genitore cambia in fase di esecuzione, lo stato e il comportamento degli oggetti figlio sono interessati.

Il "vantaggio" dell'eredità prototipica è che è possibile "patchare" lo stato e il comportamento dopo che tutti gli oggetti sono stati istanziati. Ad esempio, nel framework Ext JS è comune caricare "sostituzioni" che correggono i componenti principali del framework dopo che il framework è stato istanziato.


4
In pratica, se stai ereditando lo stato, ti stai preparando per un mondo di dolore. Lo stato ereditato verrà condiviso se risiede su un altro oggetto.
Sean McMillan,

1
Mi viene in mente che in javascript non esiste un concetto di comportamento separato dallo stato. A parte la funzione di costruzione, tutti i comportamenti possono essere assegnati / modificati dopo la creazione di oggetti come qualsiasi altro stato.
Joeri Sebrechts,

Quindi Python non ha eredità classica? Se ho class C(object): def m(self, x): return x*2e poi instance = C()quando corro instance.m(3)ottengo 6. Ma se poi cambiare Ccosì C.m = lambda s, x: x*xe corro instance.m(3)io ora ottenere 9. Lo stesso vale se faccio un class D(C)e cambio un metodo su Cqualsiasi istanza di Dricezione anche del metodo modificato. Sono frainteso o questo significa che Python non ha eredità classica secondo la tua definizione?
mVChr

@mVChr: ​​stai ancora ereditando il comportamento e non affermare in quel caso, che indica l'eredità classica, ma indica un problema con la mia definizione di "eredita il comportamento nel momento in cui l'oggetto viene istanziato". Non sono sicuro di come correggere la definizione.
Joeri Sebrechts,

9

Primo: la maggior parte delle volte, userete oggetti, non definirli, e usare oggetti è lo stesso in entrambi i paradigmi.

Secondo: la maggior parte degli ambienti prototipo usa lo stesso tipo di divisione degli ambienti basati su classi: dati mutabili sull'istanza, con metodi ereditati. Quindi c'è ancora poca differenza. (Vedi la mia risposta a questa domanda di overflow dello stack e i programmi di auto- organizzazione senza classi . Guarda Citeseer per una versione PDF .)

Terzo: la natura dinamica di Javascript ha un'influenza molto maggiore rispetto al tipo di eredità. Il fatto di poter aggiungere un nuovo metodo a tutte le istanze di un tipo assegnandolo all'oggetto base è chiaro, ma posso fare la stessa cosa in Ruby, riaprendo la classe.

Quarto: le differenze pratiche sono minime, mentre il problema pratico dell'oblio newè molto più grande - vale a dire, è molto più probabile che tu sia influenzato dalla mancanza di newquanto tu sia influenzato dalla differenza tra il prototipo e il codice classico .

Detto questo, la differenza pratica tra eredità prototipica e classica è che i tuoi metodi cose-che-tengono (classi) sono gli stessi dei tuoi dati cose-che-tengono (istanze). Ciò significa che puoi costruire le tue classi frammentario, utilizzando tutti gli stessi strumenti di manipolazione di oggetti che utilizzeresti su qualsiasi istanza. (Questo è, in effetti, il modo in cui tutte le librerie di emulazione di classe lo fanno. Per un approccio non proprio come tutti gli altri, guarda Traits.js ). Questo è interessante soprattutto se stai metaprogrammando.


yay, miglior risposta IMO!
Aivar,

0

L'eredità prototipale in JavaScript è diversa dalle classi in questi importanti modi:

I costruttori sono semplicemente funzioni che puoi chiamare senza new:

function Circle (r, x, y) { 
  //stuff here
}
Var c = new Circle();
Circle.call(c, x, y, z); //This works and you can do it over and over again.

Non ci sono variabili o metodi privati, nella migliore delle ipotesi puoi farlo:

function Circle (r, x, y) {
  var color = 'red';
  function drawCircle () {
    //some code here with x, y and r
  }
  drawCircle();
  this.setX = function (x_) {
    x = x_;
    drawCircle();
  }

}
Circle.prototype.getX = function () {
   //Can't access x!
}

Nell'esempio precedente non è possibile estendere in modo significativo la classe se si ricorre a variabili e metodi privati ​​falsi e inoltre qualsiasi metodo pubblico dichiarato verrà ricreato ogni volta che viene creata una nuova istanza.


È possibile estendere la "classe" in modo significativo. Non ci si può accedere alle variabili locali color, r, x, ye drawCircleche sono legati al campo di applicazione lessicale diCircle
Raynos

È vero, puoi, ma non puoi farlo senza creare un mucchio di metodi "pubblici" aggiunti al contesto che vengono creati ogni volta che crei un'istanza.
Bjorn,

Questo è uno schema molto strano che stai usando lì. Circle.call()come costruttore? Sembra che tu stia cercando di descrivere "oggetti funzionali" (un termine improprio ...)
Sean McMillan,

Non è qualcosa che farei mai, sto solo dicendo che è possibile chiamare ripetutamente il costruttore con JavaScript ma non nelle lingue con le classi tradizionali.
Bjorn,

1
Ma in che modo queste differenze sono "importanti"? Non vedo costruire oggetti chiamando una funzione, non usando "nuovo", come "importante", solo una piccola differenza di sintassi. Allo stesso modo, non avere variabili private non è fondamentalmente diverso, solo una piccola differenza. Inoltre, puoi avere (qualcosa di simile a) variabili private, nel modo in cui descrivi.
Roel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.