Quando utilizzare la programmazione prototipica in JavaScript


15

Ho trascorso un bel po 'di tempo a sviluppare semplici widget per progetti nel modo seguente:

var project = project || {};

(function() {

  project.elements = {
    prop1: val1,
    prop2: val2
  }

  project.method1 = function(val) {
    // Do this
  }

  project.method2 = function(val) {
    // Do that
  }

  project.init = function() {
    project.method1(project.elements.prop1)
    project.method2(project.elements.prop2)
  }
})()

project.init();

Ma ho iniziato a cambiare il mio formato nel modo seguente:

function Project() {
  this.elements = {
    prop1: val1,
    prop2: val2
  }

  this.method_one(this.elements.prop1);
  this.method_two(this.elements.prop2);
}

Project.prototype.method_one = function (val) {
  // 
};

Project.prototype.method_two = function (val) {
  //
};

new Project();

Certo, questi sono esempi sciocchi, quindi non lasciarti avvolgere attorno all'asse. Ma qual è la differenza funzionale e quando dovrei scegliere l'uno o l'altro?


Penso che il tuo primo esempio di codice non funzionerà poiché il progetto non lascia l'ambito. Per utilizzare l'ereditarietà in JS si utilizza la prototipazione. Il primo esempio non intende estendere il progetto, quindi la riusabilità potrebbe essere limitata.
Arriva il

Sì, ho inserito accidentalmente la projectdichiarazione all'interno della funzione. Aggiornato.
JDillon522,

anch'io. il primo esempio è statico, il secondo è orientato agli oggetti.
Arriva il

1
Se si utilizza solo UNA istanza di oggetto, non vi è alcun motivo per utilizzare la definizione prototipo. E quello che sembri aver bisogno - è un formato di modulo - ci sono molte opzioni per questo, vedi: addyosmani.com/writing-modular-js
c69

1
Il primo per il modello singleton Il secondo per il non-singleton (quando 1 classe può avere molti oggetti)
Kokizzu,

Risposte:


6

La prima differenza può essere riassunta come: si thisriferisce all'istanza della classe. prototypesi riferisce alla definizione .

Diciamo che abbiamo la seguente classe:

var Flight = function ( number ) { this.number = number; };

Quindi qui ci stiamo collegando this.numbera ogni istanza della classe, e ha senso perché ognuno Flightdovrebbe avere il proprio numero di volo.

var flightOne = new Flight( "ABC" );
var flightTwo = new Flight( "XYZ" );

Al contrario, prototypedefinisce una singola proprietà a cui è possibile accedere da tutte le istanze.

Ora, se vogliamo ottenere il numero di volo, possiamo semplicemente scrivere il seguente frammento e tutte le nostre istanze otterranno un riferimento a questo oggetto appena prototipato.

Flight.prototype.getNumber = function () { return this.number; };

La seconda differenza riguarda il modo in cui JavaScript cerca una proprietà di un oggetto. Quando stai cercando Object.whatever, JavaScript arriva fino all'oggetto Object principale (l'oggetto da cui tutto il resto ha ereditato) e non appena trova una corrispondenza restituisce o lo chiama.

Ma ciò accade solo per le proprietà prototipate. Quindi, se hai un posto nei livelli più alti this.whatever, JavaScript non lo considererà una corrispondenza e continuerà la ricerca.

Vediamo come succede nella realtà.

Per prima cosa, [quasi] tutto è Oggetti in JavaScript. Prova questo:

typeof null

Ora vediamo cosa c'è dentro un Object(notare il maiuscolo Oe .alla fine). In Strumenti per gli sviluppatori di Google Chrome quando si immette il .comando, verrà visualizzato un elenco di proprietà disponibili all'interno di quell'oggetto specifico.

Object.

Ora fai la stessa cosa per Function:

Function.

Potresti notare il namemetodo. Basta andare a accenderlo e vediamo cosa succede:

Object.name
Function.name

Ora creiamo una funzione:

var myFunc = function () {};

E vediamo se abbiamo anche il namemetodo qui:

myFunc.name

Dovresti ottenere una stringa vuota, ma va bene. Non dovresti ricevere un errore o un'eccezione.

Ora aggiungiamo qualcosa a quel dio Objecte vediamo se lo otteniamo anche in altri posti?

Object.prototype.test = "Okay!";

Ed ecco qua:

Object.prototype.test
Function.prototype.test
myFunc.prototype.test

In tutti i casi dovresti vedere "Okay!".

Per quanto riguarda i pro e i contro di ogni metodo, puoi considerare la prototipazione come un modo "più efficiente" di fare le cose, poiché mantiene un riferimento su ogni istanza piuttosto che copiare l'intera proprietà in ogni oggetto. D'altra parte è un esempio di accoppiamento stretto che è un grande no-no fino a quando non si può davvero giustificare il motivo. thisè piuttosto più complicato poiché rilevante per il contesto. Puoi trovare molte buone risorse gratuitamente su Internet.

Detto questo, entrambi i modi sono solo strumenti linguistici e dipende davvero da te e dal problema che stai cercando di risolvere per scegliere ciò che si adatta meglio.

Se è necessario disporre di una proprietà che sia pertinente per ogni istanza di una classe, utilizzare this. Se è necessario disporre di una proprietà per funzionare allo stesso modo in ogni istanza, utilizzare prototype.

Aggiornare

Per quanto riguarda i frammenti di esempio, il primo è un esempio di Singleton , quindi ha senso utilizzarlo thisnel corpo dell'oggetto. Puoi anche migliorare il tuo esempio rendendolo modulare in questo modo (e non è necessario utilizzarlo sempre this).

/* Assuming it will run in a web browser */
(function (window) {
    window.myApp = {
        ...
    }
})( window );

/* And in other pages ... */
(function (myApp) {
    myApp.Module = {
        ...
    }
})( myApp );

/* And if you prefer Encapsulation */
(function (myApp) {
    myApp.Module = {
         "foo": "Foo",
         "bar": function ( string ) {
             return string;
         },
         return {
             "foor": foo,
             "bar": bar
         }
    }
})( myApp );

Il tuo secondo frammento non ha molto senso perché prima stai usando thise poi stai cercando di hackerarlo prototype, il che non funziona perché thisha la priorità prototype. Non sono sicuro di quali fossero le tue aspettative da quel pezzo di codice e come funzionasse, ma ti consiglio vivamente di rifattorizzarlo.

Aggiornare

Per elaborare la thisprecedenza, prototypeposso mostrarti un esempio e dirti come può essere spiegato, ma non ho alcuna risorsa esterna per il backup.

L'esempio è molto semplice:

var myClass = function () { this.foo = "Foo"; };
myClass.prototype.foo = "nice try!";
myClass.prototype.bar = "Bar";

var obj = new myClass;
obj.foo;     // Still contains "Foo" ...
obj.bar;     // Contains "Bar" as expected

La spiegazione è, come sappiamo, thisrilevante per il contesto. Quindi non verrà all'esistenza fino a quando il contesto non sarà pronto. Quando il contesto è pronto? Quando viene creata la nuova istanza! Dovresti indovinare il resto adesso! Significa che anche se esiste una prototypedefinizione, ma thisha più senso avere la precedenza perché si tratta della nuova istanza creata in quel momento.


Questa è una risposta parzialmente epica impressionante! Puoi modificarlo e approfondire ulteriormente il confronto e il contrasto dei due metodi? I tuoi primi due paragrafi mi confondono su quale metodo di progettazione ti riferisci. Il tuo esercizio è eccellente! Non ho mai pensato molto al potere di prototipare in quel modo. Puoi anche fornire un esempio semi dettagliato sull'uso del caso in cui utilizzare ciascun metodo? Grazie ancora, è fantastico.
JDillon522,

@ JDillon522 Lieto di sentirlo. Proverò ad aggiornarlo nelle prossime ore!
53777A,

@ JDillon522 Ho appena fatto un aggiornamento veloce. Spero che questa volta sia più chiaro.
53777A,

@ JDillon522 Un po 'di più sui frammenti di esempio ...
53777A

Bel esempio singleton. Quindi ho usato thisl'esempio del prototipo sciocco perché si thisriferisce alle sue proprietà, compresi i suoi metodi. Non imparo il meglio dalla lettura del codice, ma più dall'osservazione del codice. ( MDN ci sta sopra , Object Playground (fantastico) , e pochi altri). Puoi indicare qualcosa che spieghi cosa intendi per " thisha la priorità su prototype"? Mi piacerebbe approfondire di più.
JDillon522,
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.