Aufrufmethode mit JavaScript-Prototyp


129

Ist es möglich, die Basismethode von einer Prototypmethode in JavaScript aufzurufen, wenn sie überschrieben wurde?

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
    }
};

Welches ist die Basismethode? this.do = function () {} im Konstruktor?
Ionuț G. Stan

Antworten:


200

Ich habe nicht verstanden, was genau Sie versuchen, aber normalerweise erfolgt die Implementierung objektspezifischen Verhaltens in dieser Richtung:

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
Ich verstehe das nicht, ich bin genau aus dem gleichen Grund wie markvpc hierher gekommen. Ich meine, ich möchte eine Art "private" Methode haben, die von anderen Methoden im Prototyp verwendet wird. Ihre Antwort zwingt mich, eine Fabrik zu gründen, und ich würde gerne wissen, ob es eine Möglichkeit gibt, dies zu vermeiden.
R01010010

10
Verwenden Sie in JS keine "Klassen" -Wörter, da dies zu Verwirrung führt. Es gibt keine Klassen in JavaScript
Polaris

1
Dies funktioniert nur für instanziierte Objekte. Was ist, wenn Sie alle Instanzen überschreiben möchten?
Supertonsky

Funktioniert bei mir! Genau das, was ich brauchte. Ich habe 4 Konstruktoren, die von einem Elternteil erben. Zwei von ihnen müssen eine Methode für den Basiskonstruktor, der ihr Prototyp ist, überschreiben und aufrufen. Das erlaubte mir genau das.
Binarygiant

3
Ich verstehe nicht, warum dies so viele positive Stimmen hat und als richtig markiert ist. Dies überschreibt nur das Verhalten für eine Instanz der Basisklasse, nicht für alle Instanzen der Unterklasse. Damit ist die Frage nicht beantwortet.
BlueRaja - Danny Pflughoeft

24

Eine Möglichkeit wäre, die Basismethode zu speichern und sie dann wie folgt über die Overriden-Methode aufzurufen

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

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

        //do something new

    }else{
        return this._do_base();
    }

};

11
Dies erzeugt eine unendliche Rekursion.
Johan Tidén

3
Ich war dort: Unendliche Rekursion.
Jperelli

Die else-Klausel ruft _do_base auf und führt sie aus. Dies ist ein Verweis auf prototype.do. Da sich keine Parameter (oder Status) geändert haben, wird der Code erneut in das else verschoben und ruft erneut _do_base auf.
Johan Tidén

10
@ JohanTidén _do_basespeichert einen Verweis auf die zuvor definierte Funktion. Wenn es keine gäbe, wäre es so undefined. JavaScript ist nicht - makeAusdrücke werden niemals träge ausgewertet, wie Sie es vorschlagen. Wenn der Prototyp, von dem geerbt wird, auch _do_base verwendet, um zu speichern, was seiner Meinung nach die Implementierung seines Basistyps ist do, dann haben Sie ein Problem. Ja, ein Abschluss wäre eine bessere, vererbungssichere Möglichkeit, den Verweis auf die ursprüngliche Funktionsimplementierung zu speichern, wenn diese Methode verwendet wird - obwohl dies die Verwendung erfordern würde Function.prototype.call(this).
Binki

16

Ich fürchte, Ihr Beispiel funktioniert nicht so, wie Sie denken. Dieser Teil:

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

überschreibt die Definition von

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

Da das neu erstellte Objekt bereits eine "do" -Eigenschaft hat, wird die prototypische Kette nicht nachgeschlagen.

Die klassische Form der Vererbung in Javascript ist unangenehm und schwer zu verstehen. Ich würde vorschlagen, stattdessen Douglas Crockfords einfaches Vererbungsmuster zu verwenden. So was:

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

Meiner Meinung nach eine viel klarere Art, mit Objekten, Konstruktoren und Vererbung in Javascript umzugehen. Sie können mehr in Crockfords Javascript lesen : Die guten Teile .


3
Sehr schön, aber Sie können nicht wirklich base_doals Funktion aufrufen , weil Sie auf diese Weise jegliche thisBindung in der ursprünglichen doMethode verlieren . Das Setup der Basismethode ist daher etwas komplexer, insbesondere wenn Sie sie mit dem Basisobjekt thisanstelle des untergeordneten Objekts aufrufen möchten . Ich würde etwas Ähnliches vorschlagen base_do.apply(me, arguments).
Giulio Piancastelli

Ich frage mich nur, warum benutzt du nicht varfür base_do? Ist das absichtlich und wenn ja, warum? Und, wie @GiulioPiancastelli sagte, bricht diese Unterbrechung die thisBindung innerhalb base_do()oder erbt dieser Aufruf das thisvon seinem Anrufer?
Binki

@binki Ich habe das Beispiel aktualisiert. Das varsollte da sein. Mit call(oder apply) können wir thisrichtig binden .
Magnar

10

Ich weiß, dass dieser Beitrag von vor 4 Jahren stammt, aber aufgrund meines C # -Hintergrunds suchte ich nach einer Möglichkeit, die Basisklasse aufzurufen, ohne den Klassennamen angeben zu müssen, sondern ihn über eine Eigenschaft in der Unterklasse abzurufen. Meine einzige Änderung an Christophs Antwort wäre also

Davon:

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

Dazu:

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

6
Tun Sie dies nicht, zeigen Sie this.constructormöglicherweise nicht immer darauf MyClass.
Bergi

Nun, es sollte so sein, es sei denn, jemand hat es vermasselt, die Vererbung einzurichten. 'this' sollte sich immer auf das Objekt der obersten Ebene beziehen, und seine Eigenschaft 'constructor' sollte immer auf seine eigene Konstruktorfunktion verweisen.
Triynko

5

wenn Sie eine solche Funktion definieren (mit OOP)

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

Es gibt zwei Möglichkeiten, eine Prototypfunktion aufzurufen: 1) Erstellen Sie eine Instanz und rufen Sie die Objektfunktion auf:

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

und der andere Weg ist ... 2) ruft die Funktion direkt vom Prototyp auf:

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

5

Diese Lösung verwendet Object.getPrototypeOf

TestA ist super das hat getName

TestBist ein Kind, das überschreibt, getNameaber auch getBothNamesdie superVersion von getNamesowie die childVersion aufruft

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

Eine Alternative :

// 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);
}

Obwohl es möglicherweise funktioniert, werde ich diese Lösung nicht empfehlen. Es gibt viele Gründe, warum die Verwendung __proto__(die den Prototyp eines Objekts ändert) vermieden werden sollte. Bitte benutzen Sie Object.create(...)stattdessen die Route.
KarelG

2

Wenn ich das richtig verstehe, möchten Sie, dass die Basisfunktionalität immer ausgeführt wird, während ein Teil davon den Implementierungen überlassen bleiben sollte.

Möglicherweise hilft Ihnen das Entwurfsmuster " Vorlagenmethode ".

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

Wenn Sie Ihre Superklasse beim Namen kennen, können Sie Folgendes tun:

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();

Dies wird gedruckt

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

wie erwartet.


1

Eine andere Möglichkeit mit ES5 besteht darin, die Prototypenkette mit explizit zu durchlaufen 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

Nein, Sie müssten der Funktion do im Konstruktor und der Funktion do im Prototyp unterschiedliche Namen geben.


0

Wenn Sie außerdem alle Instanzen und nicht nur diese eine spezielle Instanz überschreiben möchten , kann diese hilfreich sein.

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();

Ergebnis:

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();
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.