Metodo di chiamata tramite prototipo JavaScript


129

È possibile chiamare il metodo di base da un metodo prototipo in JavaScript se è stato ignorato?

MyClass = function(name){
    this.name = name;
    this.do = function() {
        //do somthing 
    }
};

MyClass.prototype.do = function() {  
    if (this.name === 'something') {
        //do something new
    } else {
        //CALL BASE METHOD
    }
};

Qual è il metodo di base? this.do = function () {} nel costruttore?
Ionuț G. Stan,

Risposte:


200

Non ho capito esattamente cosa stai cercando di fare, ma normalmente l'implementazione del comportamento specifico dell'oggetto viene eseguita in questo modo:

function MyClass(name) {
    this.name = name;
}

MyClass.prototype.doStuff = function() {
    // generic behaviour
}

var myObj = new MyClass('foo');

var myObjSpecial = new MyClass('bar');
myObjSpecial.doStuff = function() {
    // do specialised stuff
    // how to call the generic implementation:
    MyClass.prototype.doStuff.call(this /*, args...*/);
}

1
Non capisco, sono venuto qui esattamente per lo stesso scopo di markvpc. Voglio dire, voglio avere una sorta di metodo "privato" che viene utilizzato da altri metodi nel prototipo. La tua risposta mi costringe a creare una fabbrica e vorrei sapere se c'è un modo per evitarlo.
R01010010,

10
Non usare parole "Class" in JS perché creano confusione. Non ci sono classi in JavaScript
Polaris,

1
Funziona solo con oggetti istanziati. Cosa succede se si desidera ignorare tutte le istanze?
supertonsky,

Per me va bene! Esattamente quello di cui avevo bisogno. Ho 4 costruttori che ereditano da un genitore. Due di essi devono sovrascrivere e chiamare un metodo sul costruttore di base che è il loro prototipo. Questo mi ha permesso di fare esattamente questo.
binarygiant

3
Non capisco perché questo abbia così tanti voti ed è contrassegnato come corretto. Questo sostituisce solo il comportamento di un'istanza della classe base, non tutte le istanze della sottoclasse. Quindi questo non risponde alla domanda.
BlueRaja - Danny Pflughoeft l'

24

Beh, un modo per farlo sarebbe salvare il metodo di base e quindi chiamarlo dal metodo overriden, in questo modo

MyClass.prototype._do_base = MyClass.prototype.do;
MyClass.prototype.do = function(){  

    if (this.name === 'something'){

        //do something new

    }else{
        return this._do_base();
    }

};

11
Ciò creerà una ricorsione infinita.
Johan Tidén,

3
Ci sono stato: ricorsione infinita.
jperelli,

La clausola else chiama ed esegue _do_base, che è un riferimento a prototype.do. Poiché nessun parametro (o stato) è cambiato, il codice andrà di nuovo nell'altro, richiamando nuovamente _do_base.
Johan Tidén,

10
@ JohanTidén _do_basememorizza un riferimento a qualsiasi funzione precedentemente definita. Se non ce ne fosse nessuno, lo sarebbe undefined. JavaScript non lo è: le makeespressioni non vengono mai valutate pigramente come suggerite. Ora, se il prototipo ereditato usa anche _do_base per archiviare ciò che pensa sia l'implementazione del suo tipo base do, allora hai un problema. Quindi, sì, una chiusura sarebbe un modo migliore e sicuro per l'ereditarietà di memorizzare il riferimento all'implementazione della funzione originale se si utilizza questo metodo, anche se ciò richiederebbe l'utilizzo Function.prototype.call(this).
binki,

16

Temo che il tuo esempio non funzioni come pensi. Questa parte:

this.do = function(){ /*do something*/ };

sovrascrive la definizione di

MyClass.prototype.do = function(){ /*do something else*/ };

Poiché l'oggetto appena creato ha già una proprietà "do", non cerca la catena prototipo.

La forma classica di eredità in Javascript è imbarazzante e difficile da comprendere. Suggerirei invece di utilizzare Douglas Crockfords semplice modello di eredità. Come questo:

function my_class(name) {
    return {
        name: name,
        do: function () { /* do something */ }
    };
}

function my_child(name) {
    var me = my_class(name);
    var base_do = me.do;
    me.do = function () {
        if (this.name === 'something'){
            //do something new
        } else {
            base_do.call(me);
        }
    }
    return me;
}

var o = my_child("something");
o.do(); // does something new

var u = my_child("something else");
u.do(); // uses base function

Secondo me un modo molto più chiaro di gestire oggetti, costruttori ed eredità in javascript. Puoi leggere di più in Crockfords Javascript: Le parti buone .


3
Davvero molto bello, ma non puoi davvero chiamare base_docome una funzione, perché perdi qualsiasi thisassociazione nel dometodo originale in quel modo. Pertanto, l'installazione del metodo di base è un po 'più complessa, soprattutto se si desidera chiamarlo utilizzando l'oggetto di base thisanziché l'oggetto figlio. Suggerirei qualcosa di simile base_do.apply(me, arguments).
Giulio Piancastelli,

Chiedo solo, perché non si usa varper base_do? È apposta e, in tal caso, perché? E, come ha detto @GiulioPiancastelli, questa interruzione di connessione thisall'interno base_do()o quella chiamata erediterà il thissuo chiamante?
binki,

@binki ho aggiornato l'esempio. Il vardovrebbe essere lì. L'uso call(o apply) ci consente di legare thiscorrettamente.
Magnar,

10

So che questo post risale a 4 anni fa, ma a causa del mio background in C # stavo cercando un modo per chiamare la classe base senza dover specificare il nome della classe ma piuttosto ottenerlo da una proprietà nella sottoclasse. Quindi la mia unica modifica alla risposta di Christoph sarebbe

Da questa:

MyClass.prototype.doStuff.call(this /*, args...*/);

A questa:

this.constructor.prototype.doStuff.call(this /*, args...*/);

6
Non farlo, this.constructorpotrebbe non sempre indicare MyClass.
Bergi,

Bene, dovrebbe, a meno che qualcuno non abbia sbagliato a creare l'eredità. 'this' dovrebbe sempre fare riferimento all'oggetto di livello superiore e la sua proprietà 'constructor' dovrebbe sempre puntare alla propria funzione di costruzione.
Triynko,

5

se si definisce una funzione come questa (usando OOP)

function Person(){};
Person.prototype.say = function(message){
   console.log(message);
}

Esistono due modi per chiamare una funzione prototipo: 1) creare un'istanza e chiamare la funzione oggetto:

var person = new Person();
person.say('hello!');

e l'altro modo è ... 2) chiama la funzione direttamente dal prototipo:

Person.prototype.say('hello there!');

5

Questa soluzione utilizza Object.getPrototypeOf

TestA è super che ha getName

TestBè un bambino che ha la precedenza getNamema getBothNamesche ha anche la superversione getNamee la childversione

function TestA() {
  this.count = 1;
}
TestA.prototype.constructor = TestA;
TestA.prototype.getName = function ta_gn() {
  this.count = 2;
  return ' TestA.prototype.getName is called  **';
};

function TestB() {
  this.idx = 30;
  this.count = 10;
}
TestB.prototype = new TestA();
TestB.prototype.constructor = TestB;
TestB.prototype.getName = function tb_gn() {
  return ' TestB.prototype.getName is called ** ';
};

TestB.prototype.getBothNames = function tb_gbn() {
  return Object.getPrototypeOf(TestB.prototype).getName.call(this) + this.getName() + ' this object is : ' + JSON.stringify(this);
};

var tb = new TestB();
console.log(tb.getBothNames());


4
function NewClass() {
    var self = this;
    BaseClass.call(self);          // Set base class

    var baseModify = self.modify;  // Get base function
    self.modify = function () {
        // Override code here
        baseModify();
    };
}

4

Un'alternativa :

// shape 
var shape = function(type){
    this.type = type;
}   
shape.prototype.display = function(){
    console.log(this.type);
}
// circle
var circle = new shape('circle');
// override
circle.display = function(a,b){ 
    // call implementation of the super class
    this.__proto__.display.apply(this,arguments);
}

Nonostante possa funzionare, non consiglierò questa soluzione. Ci sono molte ragioni per cui l'uso __proto__(che modifica il [[prototipo]] `di un oggetto) dovrebbe essere evitato. Si prega di utilizzare il Object.create(...)percorso invece.
KarelG

2

Se ho capito bene, vuoi che la funzionalità Base sia sempre eseguita, mentre una parte di essa dovrebbe essere lasciata alle implementazioni.

Potresti essere aiutato dal modello di progettazione " metodo modello ".

Base = function() {}
Base.prototype.do = function() { 
    // .. prologue code
    this.impldo(); 
    // epilogue code 
}
// note: no impldo implementation for Base!

derived = new Base();
derived.impldo = function() { /* do derived things here safely */ }

1

Se conosci la tua super classe per nome, puoi fare qualcosa del genere:

function Base() {
}

Base.prototype.foo = function() {
  console.log('called foo in Base');
}

function Sub() {
}

Sub.prototype = new Base();

Sub.prototype.foo = function() {
  console.log('called foo in Sub');
  Base.prototype.foo.call(this);
}

var base = new Base();
base.foo();

var sub = new Sub();
sub.foo();

Questo stamperà

called foo in Base
called foo in Sub
called foo in Base

come previsto.


1

Un altro modo con ES5 è quello di attraversare esplicitamente la catena di prototipi usando Object.getPrototypeOf(this)

const speaker = {
  speak: () => console.log('the speaker has spoken')
}

const announcingSpeaker = Object.create(speaker, {
  speak: {
    value: function() {
      console.log('Attention please!')
      Object.getPrototypeOf(this).speak()
    }
  }
})

announcingSpeaker.speak()

0

No, è necessario assegnare nomi diversi alla funzione do nel costruttore e alla funzione do nel prototipo.


0

Inoltre, se si desidera sovrascrivere tutte le istanze e non solo quella speciale, questa potrebbe essere d'aiuto.

function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

myObj = new MyClass();
myObj.myMethod();

risultato:

doing original
doing override

-1
function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

myObj = new MyClass();
myObj.myMethod();
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.