Come chiamare un metodo parent dalla classe child in javascript?


156

Ho passato le ultime due ore a cercare di trovare una soluzione al mio problema, ma sembra essere senza speranza.

Fondamentalmente ho bisogno di sapere come chiamare un metodo genitore da una classe figlio. Tutte le cose che ho provato finora finiscono per non funzionare o sovrascrivere il metodo genitore.

Sto usando il seguente codice per impostare OOP in javascript:

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

Devo prima chiamare il metodo del genitore e poi aggiungere altre cose ad esso nella classe del bambino.

Nella maggior parte dei linguaggi OOP sarebbe semplice come chiamare parent.myMethod() Ma non riesco davvero a capire come sia fatto in JavaScript.

Qualsiasi aiuto è molto apprezzato, grazie!

Risposte:


196

Ecco come è fatto: ParentClass.prototype.myMethod();

Oppure, se vuoi chiamarlo nel contesto dell'istanza corrente, puoi fare: ParentClass.prototype.myMethod.call(this)

Lo stesso vale per la chiamata di un metodo parent dalla classe child con argomenti: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)* Suggerimento: utilizzare apply()invece di call()passare argomenti come array.


7
Se vuoi chiamarlo nel contesto dell'istanza corrente, devi fare ParentClass.prototype.myMethod.apply() or ParentClass.prototype.myMethod.call () `, come fai con il tuo costruttore.
JMM,

3
Aggiungo semplicemente che se vuoi chiamare con argomenti, entrano nella funzione apply o call ( ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);)
Gershom,

Non capisco. Se chiamo ParentClass.prototype.myMethod.call (questo); da myMethod di ChildObject, ho ricevuto un errore "Uncaught TypeError: Impossibile leggere la proprietà 'call' di undefined".
Zhekaus,

@zhekaus, significherebbe che non hai myMethodin classe.
YemSalat

2
Attualmente sto usando this.myFun = function () {} per dichiarare un metodo oggetto, quindi chiama ParentClass.prototype.myFun.call (...) non funziona, quindi devo usare CurrentClass.prototype.myFun.call ( ...). JS è ... una merda, dovremmo usare il vero OOP.
Loenix

156

Lo stile ES6 ti consente di utilizzare nuove funzionalità, come la superparola chiave. superla parola chiave riguarda il contesto della classe genitore, quando si utilizza la sintassi delle classi ES6. A titolo di esempio molto semplice, controlla:

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

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

Inoltre, puoi usare superper chiamare il costruttore principale:

class Foo {}

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

E ovviamente puoi usarlo per accedere alle proprietà della classe genitore super.prop. Quindi, usa ES6 e sii felice.


10
@ fsinisi90 Credo che la domanda non riguardi i metodi di classe dei genitori, ma piuttosto i metodi di istanza dei genitori che non possono essere chiamati con la super parola chiave da ES6.
mcmlxxxiii,

funziona anche con metodi non statici (testato con Chrome, senza transpiliing, non provato con la parola chiave statica)
Gianluca Casati

Perché deve superessere chiamato? Esiste un'equivalenza nel "vecchio" JS?
1252748

3
super () deve essere chiamato nel costruttore della classe figlio prima di ogni altra cosa.
user938363

1
@GianlucaCasati: puoi usarlo solo super()con metodi statici; sembra che tu l'abbia usato nel costruttore.
ZzZombo,

5

Bene, per fare questo, non sei limitato Classall'astrazione di ES6. L'accesso ai metodi prototipo del costruttore principale è possibile tramite la __proto__proprietà (sono abbastanza sicuro che ci saranno colleghi programmatori JS a lamentarsi che è deprezzato) che è deprezzato ma allo stesso tempo ha scoperto che in realtà è uno strumento essenziale per le esigenze di sottoclasse ( soprattutto per le esigenze di sottoclasse degli array). Quindi, mentre la __proto__proprietà è ancora disponibile in tutti i principali motori JS che conosco, ES6 ha introdotto la Object.getPrototypeOf()funzionalità al di sopra di esso. Lo super()strumento Classnell'astrazione è uno zucchero sintattico di questo.

Quindi, nel caso in cui non si abbia accesso al nome del costruttore principale e non si desideri utilizzare l' Classastrazione, è comunque possibile fare come segue;

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

4

In caso di livello di ereditarietà multipla, questa funzione può essere utilizzata come metodo super () in altre lingue. Ecco un violino demo , con alcuni test, puoi usarlo in questo modo, all'interno del tuo metodo di utilizzo:call_base(this, 'method_name', arguments);

Si avvale di funzioni ES piuttosto recenti, una compatibilità con i browser più vecchi non è garantita. Testato 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);
    }
}

2

Che ne dici di qualcosa basato sull'idea di Douglas Crockford:

    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

2

Ecco un bel modo per gli oggetti figlio di avere accesso alle proprietà e ai metodi principali usando la catena di prototipi di JavaScript ed è compatibile con Internet Explorer. JavaScript cerca nella catena di prototipi i metodi e vogliamo che la catena di prototipi del bambino assomigli a questo:

Istanza figlio -> Prototipo figlio (con metodi figlio) -> Prototipo genitore (con metodi padre) -> Prototipo oggetto -> null

I metodi figlio possono anche chiamare metodi parent ombreggiati, come mostrato nei tre asterischi *** di seguito.

Ecco come:

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


1

Esiste una soluzione molto più semplice e compatta per la ricerca di prototipi multilivello, ma richiede Proxysupporto. Uso: SUPER(<instance>).<method>(<args>)per esempio, assumere due classi Ae B extends Acon il metodo m: SUPER(new B).m().

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

0

Mentre è possibile chiamare il metodo genitore dal prototipo del genitore, è necessario passare l'istanza figlio corrente per l'utilizzo call, applyo bindmetodo. Il bindmetodo creerà una nuova funzione, quindi non lo consiglio se ti preoccupi delle prestazioni, tranne che ha chiamato solo una volta.

In alternativa, è possibile sostituire il metodo figlio e inserire il metodo padre sull'istanza mentre si chiama il metodo figlio originale.

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

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.