Metodi di sostituzione JavaScript


87

Diciamo che hai il codice seguente:

function A() {
    function modify() {
       x = 300;
       y = 400;
    }

    var c = new C();
}

function B() {
    function modify(){
       x = 3000;
       y = 4000;
    }

    var c = new C();
}

C = function () {
   var x = 10;
   var y = 20;

   function modify() {
      x = 30;
      y = 40;
   };

   modify();
   alert("The sum is: " + (x+y));
}

Ora la domanda è: se c'è un modo in cui posso sovrascrivere il metodo modifyda Ccon i metodi che sono in Ae B. In Java superuseresti la parola chiave -key, ma come puoi ottenere qualcosa di simile in JavaScript?


6
modifynon è un metodo ma una funzione annidata - c'è una differenza tra questi due ...
Šime Vidas

2
In Java si utilizza la superparola chiave per accedere ai campi e ai metodi non privati ​​di una superclasse. Non lo usi per sovrascriverli.
FK82

Risposte:


138

Modifica: sono passati sei anni da quando è stata scritta la risposta originale e molto è cambiato!

  • Se stai usando una versione più recente di JavaScript, possibilmente compilata con uno strumento come Babel , puoi usare classi reali .
  • Se stai usando i costruttori di componenti di tipo classe forniti da Angular o React , ti consigliamo di cercare nella documentazione quel framework.
  • Se stai usando ES5 e realizzi classi "false" a mano usando prototipi, la risposta qui sotto è ancora più giusta che mai.

In bocca al lupo!


L'ereditarietà di JavaScript sembra leggermente diversa da Java. Ecco come appare il sistema di oggetti JavaScript nativo:

// Create a class
function Vehicle(color){
  this.color = color;
}

// Add an instance method
Vehicle.prototype.go = function(){
  return "Underway in " + this.color;
}

// Add a second class
function Car(color){
  this.color = color;
}

// And declare it is a subclass of the first
Car.prototype = new Vehicle();

// Override the instance method
Car.prototype.go = function(){
  return Vehicle.prototype.go.call(this) + " car"
}

// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"

var c = new Car("red");
c.go() // "Underway in red car"

Sfortunatamente questo è un po 'brutto e non include un modo molto carino per "super": devi specificare manualmente quale metodo delle classi genitore vuoi chiamare. Di conseguenza, ci sono una varietà di strumenti per rendere più piacevole la creazione di classi. Prova a guardare Prototype.js, Backbone.js o una libreria simile che includa una sintassi migliore per eseguire OOP in js.


2
In alternativa all'utilizzo di uno strumento per "rendere la creazione di classi più piacevole", non è possibile creare classi. L'emulazione OO classica in js diventa sempre complicata.
Raynos

1
(commento non costruttivo) per essere il linguaggio di "basso livello" nel mondo dei browser è molto brutto senza motivo. Lo sto ancora imparando, grazie!
dnuske

9
Credo invece di Car.prototype = new Vehicle (); questo dovrebbe essere Car.prototype = Object.create (Vehicle.prototype); no?
Jordan

@Martin ha ragione, vedi javascript-
inheritance

Uno dei motivi per cui Car.prototype = new Vehicle (); non è corretto perché non ha nulla da passare a Vehicle () mentre riceve un colore.
Tushar Arora

63

Poiché si tratta di un successo di punta su Google, vorrei fornire una risposta aggiornata.

L'uso delle classi ES6 semplifica notevolmente l'ereditarietà e l'override del metodo:

'use strict';

class A {
    speak() {
        console.log("I'm A");
    }
}

class B extends A {
    speak() {
        super.speak();

        console.log("I'm B");
    }
}

var a = new A();
a.speak();
// Output:
// I'm A

var b = new B();
b.speak();
// Output:
// I'm A
// I'm B

La superparola chiave si riferisce alla classe genitore quando viene utilizzata nella classe che eredita. Inoltre, tutti i metodi sulla classe genitore sono legati all'istanza del figlio, quindi non è necessario scrivere super.method.apply(this);.

Per quanto riguarda la compatibilità: la tabella di compatibilità ES6 mostra solo le versioni più recenti delle classi di supporto dei principali lettori (principalmente). I browser V8 li hanno da gennaio di quest'anno (Chrome e Opera) e Firefox, utilizzando il motore JS SpiderMonkey, vedrà le classi il mese prossimo con la loro versione ufficiale di Firefox 45. Sul lato mobile, Android non supporta ancora questa funzione, mentre iOS 9, rilasciato cinque mesi fa, ha un supporto parziale.

Fortunatamente, c'è Babel , una libreria JS per ricompilare il codice Harmony in codice ES5. Le classi e molte altre fantastiche funzionalità in ES6 possono rendere il tuo codice Javascript molto più leggibile e gestibile.


10
Questa risposta merita più credito ed è attualmente la risposta giusta.
Klompenrunner

6

Once dovrebbe evitare di emulare l'OO classico e utilizzare invece l'OO prototipico. Una bella libreria di utilità per OO prototipico è traits .

Piuttosto che sovrascrivere i metodi e impostare catene di ereditarietà (si dovrebbe sempre privilegiare la composizione degli oggetti rispetto all'ereditarietà degli oggetti) dovresti raggruppare le funzioni riutilizzabili in tratti e creare oggetti con quelli.

Esempio dal vivo

var modifyA = {
    modify: function() {
        this.x = 300;
        this.y = 400;
    }
};

var modifyB = {
    modify: function() {
        this.x = 3000;
        this.y = 4000;
    }
};

C = function(trait) {
    var o = Object.create(Object.prototype, Trait(trait));

    o.modify();
    console.log("sum : " + (o.x + o.y));

    return o;
}

//C(modifyA);
C(modifyB);

10
Non stai rispondendo alla domanda. Questo dovrebbe essere un commento se non altro.
FK82


3

modify () nel tuo esempio è una funzione privata, che non sarà accessibile da nessuna parte ma all'interno della tua definizione A, B o C. Dovresti dichiararlo come

this.modify = function(){}

C non ha alcun riferimento ai suoi genitori, a meno che non lo passi a C. Se C è impostato per ereditare da A o B, erediterà i suoi metodi pubblici (non le sue funzioni private come hai definito modify ()). Una volta che C eredita i metodi dal suo genitore, puoi sovrascrivere i metodi ereditati.


modificare è una funzione locale. Non esiste un privato in javascript
Raynos

locale / privato, non è la stessa cosa, solo un termine diverso?
Alex Heyd

2

il metodo modify()che hai chiamato nell'ultimo viene chiamato nel contesto globale se vuoi sovrascrivere modify()devi prima ereditare Ao B.

Forse stai cercando di fare questo:

In questo caso CereditaA

function A() {
    this.modify = function() {
        alert("in A");
    }
}

function B() {
    this.modify = function() {
        alert("in B");
    }
}

C = function() {
    this.modify = function() {
        alert("in C");
    };

    C.prototype.modify(); // you can call this method where you need to call modify of the parent class
}

C.prototype = new A();

1
C.prototype.modify()avrebbe un thisvalore sbagliato . Vale a dire C.prototypeinvece dell'istanza di c. Si prega di utilizzare, .call(this)ma la risposta è solo un duplicato :)
Raynos

1

No, a meno che tu non renda tutte le variabili "pubbliche", cioè le rendi membri di Functiondirettamente o tramite la prototypeproprietà.

var C = function( ) {
    this.x = 10 , this.y = 20 ;
    this.modify = function( ) {
        this.x = 30 , this.y = 40 ;
        console.log("(!) C >> " + (this.x + this.y) ) ;
    } ;
} ;

var A = function( ) {
    this.modify = function( ) {
       this.x = 300 , this.y = 400 ;
       console.log("(!) A >> " + (this.x + this.y) ) ;
    } ;
} ;
    A.prototype = new C ;

var B = function( ) {
    this.modify = function( ) {
       this.x = 3000 , this.y = 4000 ;
       console.log("(!) B >> " + (this.x + this.y) ) ;
    } ;
} ;


new C( ).modify( ) ;
new A( ).modify( ) ;
new B( ).modify( ) ; 

Noterai alcuni cambiamenti.

Ancora più importante, la chiamata al presunto costruttore di "superclassi" è ora implicita all'interno di questa riga:

<name>.prototype = new C ;

Entrambi Ae Bora avranno membri modificabili individualmentex e yche non sarebbe il caso se avessimo ... = Cinvece scritto .

Poi, x, ye modifysono tutti i membri del "pubblico" in modo che l'assegnazione di un diverso Functionper loro

 <name>.prototype.modify = function( ) { /* ... */ }

"sovrascriverà" l'originale Functioncon quel nome.

Infine, la chiamata a modifynon può essere eseguita nella Functiondichiarazione perché la chiamata implicita alla "superclasse" verrebbe quindi eseguita di nuovo quando impostiamo la presunta "superclasse" sulla prototypeproprietà delle presunte "sottoclassi".

Ma beh, questo è più o meno come faresti questo genere di cose in JavaScript.

HTH,

FK


In ogni caso, non ha senso mettere in <name>.prototype = new C;ombra tutti i membri del prototipo
Raynos

@ Raynos: Sì, c'è. Vale a dire, tutta l'ereditarietà Objectscondividerebbe lo stesso membro Cse non si istanzia un Coggetto. Così, cambiando xin Acambierebbe xin Ce modificare così xin B. Il che è ovviamente indesiderabile.
FK82

hai perso il punto. È possibile rimuovere la riga e il codice continuerà a funzionare
Raynos

@ Raynos: Temo che ti stia perdendo il tuo punto. ;-) Vogliamo Ae Bda cui ereditare C. Se quella linea fosse mancante, non sarebbe così. In effetti A, Bsarebbe l'unico prototipo a cui "shadow" avrebbe accesso in quel caso Object.prototype.
FK82

guarda il codice. A e B non utilizzano nessuno dei membri di C nel prototipo. Quindi "ereditare" da C è inutile. Questo perché A e B ridefinire x, yed modifye quindi ombra tutti i Cmembri 's. Qual è il punto di mettere C nel prototipo se non lo usi? È codice morto.
Raynos

0

function A() {
    var c = new C();
	c.modify = function(){
		c.x = 123;
		c.y = 333;
	}
	c.sum();
}

function B() {
    var c = new C();
	c.modify = function(){
		c.x = 999;
		c.y = 333;
	}
	c.sum();
}


C = function () {
   this.x = 10;
   this.y = 20;

   this.modify = function() {
      this.x = 30;
      this.y = 40;
   };
   
   this.sum = function(){
	this.modify();
	console.log("The sum is: " + (this.x+this.y));
   }
}

A();
B();

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.