Wie rufe ich eine übergeordnete Methode aus einer untergeordneten Klasse in Javascript auf?

156

Ich habe die letzten paar Stunden damit verbracht, eine Lösung für mein Problem zu finden, aber es scheint hoffnungslos zu sein.

Grundsätzlich muss ich wissen, wie man eine übergeordnete Methode aus einer untergeordneten Klasse aufruft. All das, was ich bisher versucht habe, führt dazu, dass die übergeordnete Methode entweder nicht funktioniert oder überschrieben wird.

Ich verwende den folgenden Code, um OOP in Javascript einzurichten:

// SET UP OOP
// surrogate constructor (empty function)
function surrogateCtor() {}

function extend(base, sub) {
    // copy the prototype from the base to setup inheritance
    surrogateCtor.prototype = base.prototype;
    sub.prototype = new surrogateCtor();
    sub.prototype.constructor = sub;
}

// parent class
function ParentObject(name) {
    this.name = name;
}
// parent's methods
ParentObject.prototype = {
    myMethod: function(arg) {
        this.name = arg;
    }
}

// child
function ChildObject(name) {
    // call the parent's constructor
    ParentObject.call(this, name);
    this.myMethod = function(arg) {
        // HOW DO I CALL THE PARENT METHOD HERE?
        // do stuff
    }
}

// setup the prototype chain
extend(ParentObject, ChildObject);

Ich muss zuerst die Methode des Elternteils aufrufen und dann in der untergeordneten Klasse weitere Dinge hinzufügen.

In den meisten OOP-Sprachen wäre das so einfach wie das Aufrufen. parent.myMethod() Aber ich kann wirklich nicht verstehen, wie es in Javascript gemacht wird.

Jede Hilfe wird sehr geschätzt, danke!

YemSalat
quelle

Antworten:

196

So geht's: ParentClass.prototype.myMethod();

Oder wenn Sie es im Kontext der aktuellen Instanz aufrufen möchten, können Sie Folgendes tun: ParentClass.prototype.myMethod.call(this)

Gleiches gilt für den Aufruf einer übergeordneten Methode aus einer untergeordneten Klasse mit Argumenten: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)* Hinweis: Verwenden Sie diese apply()Option call(), um Argumente als Array zu übergeben.

YemSalat
quelle
7
Wenn Sie es im Kontext der aktuellen Instanz aufrufen möchten, müssen Sie ParentClass.prototype.myMethod.apply() or ParentClass.prototype.myMethod.call () `ausführen, wie Sie es mit Ihrem Konstruktor tun.
JMM
3
ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);
Gershom
Ich verstehe nicht. Wenn ich ParentClass.prototype.myMethod.call (this) aufrufe; In der myMethod von ChildObject wurde die Fehlermeldung "Nicht erfasster Typfehler: Eigenschaft 'Aufruf' von undefiniert kann nicht gelesen werden" angezeigt.
Zhekaus
@zhekaus, es würde bedeuten, dass Sie nicht myMethodin Ihrer Klasse haben.
YemSalat
2
Ich verwende derzeit this.myFun = function () {}, um eine Objektmethode zu deklarieren. Der Aufruf von ParentClass.prototype.myFun.call (...) funktioniert also nicht. Daher muss ich CurrentClass.prototype.myFun.call ( ...). JS ist ... ein Mist, wir sollten echtes OOP verwenden.
Loenix
155

Mit dem ES6-Stil können Sie neue Funktionen wie das superSchlüsselwort verwenden. superBei der Verwendung der ES6-Klassensyntax dreht sich alles um den Kontext der übergeordneten Klasse. Als sehr einfaches Beispiel: Kasse:

class Foo {
    static classMethod() {
        return 'hello';
    }
}

class Bar extends Foo {
    static classMethod() {
        return super.classMethod() + ', too';
    }
}
Bar.classMethod(); // 'hello, too'

Sie können auch superden übergeordneten Konstruktor aufrufen:

class Foo {}

class Bar extends Foo {
    constructor(num) {
        let tmp = num * 2; // OK
        this.num = num; // ReferenceError
        super();
        this.num = num; // OK
    }
}

Und natürlich können Sie damit auf übergeordnete Klasseneigenschaften zugreifen super.prop. Verwenden Sie also ES6 und seien Sie glücklich.

Dmytro Medvid
quelle
10
@ fsinisi90 Ich glaube, die Frage bezieht sich nicht auf die Klassenmethoden der Eltern, sondern auf die Instanzmethoden der Eltern, die ab ES6 nicht mehr mit dem Schlüsselwort super aufgerufen werden können.
mcmlxxxiii
Es funktioniert auch für Methoden, die nicht statisch sind (getestet mit Chrome, ohne Transpiliierung, nicht versucht, das statische Schlüsselwort)
Gianluca Casati
Warum muss es superaufgerufen werden? Gibt es eine Äquivalenz in "alten" JS?
1252748
3
super () muss vor allem anderen im untergeordneten Klassenkonstruktor aufgerufen werden.
user938363
1
@GianlucaCasati: Sie können nur super()in statischen Methoden verwenden; Sieht so aus, als hätten Sie es im Konstruktor verwendet.
ZzZombo
5

Um dies zu tun, sind Sie nicht auf die ClassAbstraktion von ES6 beschränkt. Der Zugriff auf die Prototypmethoden des übergeordneten Konstruktors ist über die __proto__Eigenschaft möglich (ich bin mir ziemlich sicher, dass es andere JS-Codierer geben wird, die sich darüber beschweren, dass sie abgeschrieben ist), die abgeschrieben wird, aber gleichzeitig festgestellt hat, dass sie tatsächlich ein wesentliches Werkzeug für die Unterklassifizierung von Anforderungen ist ( speziell für die Anforderungen der Array-Unterklassifizierung). Während die __proto__Eigenschaft noch in allen wichtigen JS-Engines verfügbar ist, die ich kenne, hat ES6 die Object.getPrototypeOf()Funktionalität darüber hinaus eingeführt. Das super()Werkzeug in der ClassAbstraktion ist ein syntaktischer Zucker davon.

Falls Sie also keinen Zugriff auf den Namen des übergeordneten Konstruktors haben und die ClassAbstraktion nicht verwenden möchten, können Sie dennoch Folgendes tun:

function ChildObject(name) {
    // call the parent's constructor
    ParentObject.call(this, name);
    this.myMethod = function(arg) {
    //this.__proto__.__proto__.myMethod.call(this,arg);
    Object.getPrototypeOf(Object.getPrototypeOf(this)).myMethod.call(this,arg);
    }
}
Reduzieren
quelle
4

Bei mehreren Vererbungsstufen kann diese Funktion in anderen Sprachen als super () -Methode verwendet werden. Hier ist eine Demo-Geige , mit einigen Tests können Sie sie wie folgt in Ihrer Methode verwenden:call_base(this, 'method_name', arguments);

Es nutzt die neuesten ES-Funktionen, eine Kompatibilität mit älteren Browsern ist nicht garantiert. Getestet in IE11, FF29, CH35.

/**
 * Call super method of the given object and method.
 * This function create a temporary variable called "_call_base_reference",
 * to inspect whole inheritance linage. It will be deleted at the end of inspection.
 *
 * Usage : Inside your method use call_base(this, 'method_name', arguments);
 *
 * @param {object} object The owner object of the method and inheritance linage
 * @param {string} method The name of the super method to find.
 * @param {array} args The calls arguments, basically use the "arguments" special variable.
 * @returns {*} The data returned from the super method.
 */
function call_base(object, method, args) {
    // We get base object, first time it will be passed object,
    // but in case of multiple inheritance, it will be instance of parent objects.
    var base = object.hasOwnProperty('_call_base_reference') ? object._call_base_reference : object,
    // We get matching method, from current object,
    // this is a reference to define super method.
            object_current_method = base[method],
    // Temp object wo receive method definition.
            descriptor = null,
    // We define super function after founding current position.
            is_super = false,
    // Contain output data.
            output = null;
    while (base !== undefined) {
        // Get method info
        descriptor = Object.getOwnPropertyDescriptor(base, method);
        if (descriptor !== undefined) {
            // We search for current object method to define inherited part of chain.
            if (descriptor.value === object_current_method) {
                // Further loops will be considered as inherited function.
                is_super = true;
            }
            // We already have found current object method.
            else if (is_super === true) {
                // We need to pass original object to apply() as first argument,
                // this allow to keep original instance definition along all method
                // inheritance. But we also need to save reference to "base" who
                // contain parent class, it will be used into this function startup
                // to begin at the right chain position.
                object._call_base_reference = base;
                // Apply super method.
                output = descriptor.value.apply(object, args);
                // Property have been used into super function if another
                // call_base() is launched. Reference is not useful anymore.
                delete object._call_base_reference;
                // Job is done.
                return output;
            }
        }
        // Iterate to the next parent inherited.
        base = Object.getPrototypeOf(base);
    }
}
weeger
quelle
2

Wie wäre es mit etwas, das auf der Idee von Douglas Crockford basiert:

    function Shape(){}

    Shape.prototype.name = 'Shape';

    Shape.prototype.toString = function(){
        return this.constructor.parent
            ? this.constructor.parent.toString() + ',' + this.name
            : this.name;
    };


    function TwoDShape(){}

    var F = function(){};

    F.prototype = Shape.prototype;

    TwoDShape.prototype = new F();

    TwoDShape.prototype.constructor = TwoDShape;

    TwoDShape.parent = Shape.prototype;

    TwoDShape.prototype.name = '2D Shape';


    var my = new TwoDShape();

    console.log(my.toString()); ===> Shape,2D Shape
Alessandro
quelle
2

Hier ist eine gute Möglichkeit für untergeordnete Objekte, mithilfe der Prototypkette von JavaScript auf übergeordnete Eigenschaften und Methoden zuzugreifen, und sie ist mit Internet Explorer kompatibel. JavaScript durchsucht die Prototypenkette nach Methoden und wir möchten, dass die Prototypenkette des Kindes folgendermaßen aussieht:

Untergeordnete Instanz -> Untergeordneter Prototyp (mit untergeordneten Methoden) -> Übergeordneter Prototyp (mit übergeordneten Methoden) -> Objektprototyp -> null

Die untergeordneten Methoden können auch schattierte übergeordnete Methoden aufrufen, wie in den drei Sternchen *** unten gezeigt.

Hier ist wie:

//Parent constructor
function ParentConstructor(firstName){
    //add parent properties:
    this.parentProperty = firstName;
}

//add 2 Parent methods:
ParentConstructor.prototype.parentMethod = function(argument){
    console.log(
            "Parent says: argument=" + argument +
            ", parentProperty=" + this.parentProperty +
            ", childProperty=" + this.childProperty
    );
};

ParentConstructor.prototype.commonMethod = function(argument){
    console.log("Hello from Parent! argument=" + argument);
};

//Child constructor    
function ChildConstructor(firstName, lastName){
    //first add parent's properties
    ParentConstructor.call(this, firstName);

    //now add child's properties:
    this.childProperty = lastName;
}

//insert Parent's methods into Child's prototype chain
var rCopyParentProto = Object.create(ParentConstructor.prototype);
rCopyParentProto.constructor = ChildConstructor;
ChildConstructor.prototype = rCopyParentProto;

//add 2 Child methods:
ChildConstructor.prototype.childMethod = function(argument){
    console.log(
            "Child says: argument=" + argument +
            ", parentProperty=" + this.parentProperty +
            ", childProperty=" + this.childProperty
    );
};

ChildConstructor.prototype.commonMethod = function(argument){
    console.log("Hello from Child! argument=" + argument);

    // *** call Parent's version of common method
    ParentConstructor.prototype.commonMethod(argument);
};

//create an instance of Child
var child_1 = new ChildConstructor('Albert', 'Einstein');

//call Child method
child_1.childMethod('do child method');

//call Parent method
child_1.parentMethod('do parent method');

//call common method
child_1.commonMethod('do common method');

CaptureWiz
quelle
1

Es gibt eine viel einfachere und kompaktere Lösung für die Suche nach mehrstufigen Prototypen, die jedoch ProxyUnterstützung erfordert . Verwendung: SUPER(<instance>).<method>(<args>)Zum Beispiel unter der Annahme von zwei Klassen Aund B extends Amit der Methode m: SUPER(new B).m().

function SUPER(instance) {
    return new Proxy(instance, {
        get(target, prop) {
            return Object.getPrototypeOf(Object.getPrototypeOf(target))[prop].bind(target);
        }
    });
}
ZzZombo
quelle
0

Während Sie die übergeordnete Methode , mit dem Prototyp des Mutter aufrufen können, müssen Sie die aktuelle untergeordnete Instanz für die Verwendung zu übergeben call, applyoder bindVerfahren. Die bindMethode erstellt eine neue Funktion, daher empfehle ich dies nicht, wenn Sie sich für die Leistung interessieren, es sei denn, sie wird nur einmal aufgerufen.

Alternativ können Sie die untergeordnete Methode ersetzen und die übergeordnete Methode in die Instanz einfügen, während Sie die ursprüngliche untergeordnete Methode aufrufen.

function proxy(context, parent){
  var proto = parent.prototype;
  var list = Object.getOwnPropertyNames(proto);
  
  var child = {};
  for(var i=0; i<list.length; i++){
    var key = list[i];

    // Create only when child have similar method name
    if(context[key] !== proto[key]){
      child[key] = context[key];
      context[key] = function(){
        context.super = proto[key];
        return child[key].apply(context, arguments);
      }
    }
  }
}

// ========= The usage would be like this ==========

class Parent {
  first = "Home";

  constructor(){
    console.log('Parent created');
  }

  add(arg){
    return this.first + ", Parent "+arg;
  }
}

class Child extends Parent{
  constructor(b){
    super();
    proxy(this, Parent);
    console.log('Child created');
  }

  // Comment this to call method from parent only
  add(arg){
    return this.super(arg) + ", Child "+arg;
  }
}

var family = new Child();
console.log(family.add('B'));

StefansArya
quelle