¿Cómo llamar a un método principal desde la clase secundaria en javascript?


156

Pasé las últimas dos horas tratando de encontrar una solución a mi problema, pero parece que no tiene remedio.

Básicamente, necesito saber cómo llamar a un método principal desde una clase secundaria. Todo lo que he intentado hasta ahora termina en no funcionar o sobreescribir el método principal.

Estoy usando el siguiente código para configurar OOP en 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);

Necesito llamar primero al método de los padres y luego agregarle algunas cosas más en la clase secundaria.

En la mayoría de los lenguajes OOP eso sería tan simple como llamar parent.myMethod() Pero realmente no puedo entender cómo se hace en javascript.

Cualquier ayuda es muy apreciada, gracias!

Respuestas:


196

Así es como se hace: ParentClass.prototype.myMethod();

O si desea llamarlo en el contexto de la instancia actual, puede hacer lo siguiente: ParentClass.prototype.myMethod.call(this)

Lo mismo ocurre con la llamada a un método principal desde la clase secundaria con argumentos: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)* Sugerencia: use en apply()lugar de call()pasar argumentos como una matriz.


77
Si desea llamarlo en el contexto de la instancia actual, debe hacer ParentClass.prototype.myMethod.apply() or ParentClass.prototype.myMethod.call () `, como lo hace con su constructor.
JMM

3
Solo agregue que si desea llamar con argumentos, van dentro de la función aplicar o llamar ( ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);)
Gershom

No entiendo. Si llamo ParentClass.prototype.myMethod.call (esto); del myMethod de ChildObject, recibí un error "Error de tipo no capturado: no se puede leer la propiedad 'call' de undefined".
zhekaus

@zhekaus, significaría que no tienes myMethoden tu clase.
YemSalat

2
Actualmente estoy usando this.myFun = function () {} para declarar un método de objeto, así que llamar a ParentClass.prototype.myFun.call (...) no funciona, así que tengo que usar CurrentClass.prototype.myFun.call ( ...) JS es ... una mierda, deberíamos usar OOP real.
Loenix

156

El estilo ES6 le permite utilizar nuevas funciones, como superpalabras clave. superLa palabra clave tiene que ver con el contexto de la clase principal, cuando se utiliza la sintaxis de clases ES6 Como un ejemplo muy simple, finaliza la compra:

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

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

Además, puede usar superpara llamar al constructor principal:

class Foo {}

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

Y, por supuesto, puede usarlo para acceder a las propiedades de la clase principal super.prop. Entonces, usa ES6 y sé feliz.


10
@ fsinisi90 Creo que la pregunta no se trata de los métodos de clase de los padres, sino de los métodos de instancia de los padres que simplemente no se pueden llamar con súper palabras clave a partir de ES6.
mcmlxxxiii

funciona también para métodos que no son estáticos (probado con Chrome, sin transpiración, sin probar la palabra clave estática)
Gianluca Casati

¿Por qué necesita superser llamado? ¿Hay una equivalencia en el "viejo" JS?
1252748

3
super () tiene que ser llamado en el constructor de clase hijo antes que nada.
user938363

1
@GianlucaCasati: solo se puede usar super()en métodos estáticos; Parece que lo usaste en constructor.
ZzZombo

5

Bueno, para hacer esto, no está limitado con la Classabstracción de ES6. El acceso a los métodos prototipo del constructor principal es posible a través de la __proto__propiedad (estoy bastante seguro de que habrá otros codificadores de JS para quejarse de que está depreciado) que se deprecia pero al mismo tiempo se descubre que en realidad es una herramienta esencial para las necesidades de subclasificación ( especialmente para las necesidades de subclases de Array). Entonces, aunque la __proto__propiedad aún está disponible en todos los principales motores JS que conozco, ES6 introdujo la Object.getPrototypeOf()funcionalidad además. La super()herramienta en la Classabstracción es un azúcar sintáctico de esto.

Entonces, en caso de que no tenga acceso al nombre del constructor principal y no quiera usar la Classabstracción, puede hacer lo siguiente;

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

En caso de un nivel de herencia múltiple, esta función se puede usar como un método super () en otros idiomas. Aquí hay un violín de demostración , con algunas pruebas, puede usarlo así, dentro del uso de su método:call_base(this, 'method_name', arguments);

Hace uso de funciones ES bastante recientes, no se garantiza la compatibilidad con navegadores antiguos. Probado en 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

¿Qué tal algo basado en la idea de 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

Esta es una buena manera para que los objetos secundarios tengan acceso a las propiedades y métodos principales utilizando la cadena de prototipos de JavaScript, y es compatible con Internet Explorer. JavaScript busca métodos en la cadena de prototipos y queremos que la cadena de prototipos del niño tenga este aspecto:

Instancia secundaria -> Prototipo secundario (con métodos secundarios) -> Prototipo primario (con métodos principales) -> Prototipo de objeto -> nulo

Los métodos secundarios también pueden llamar métodos primarios sombreados, como se muestra en los tres asteriscos *** a continuación.

Así es cómo:

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

Existe una solución mucho más fácil y compacta para la búsqueda de prototipos multinivel, pero requiere Proxysoporte. Uso: SUPER(<instance>).<method>(<args>), por ejemplo, suponiendo dos clases Ay B extends Acon el método de 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

Si bien se puede llamar al método de los padres por el prototipo del padre, usted tendrá que pasar la instancia actual del niño para usar call, applyo bindmétodo. El bindmétodo creará una nueva función, por lo que no recomiendo que si le importa el rendimiento, excepto que solo se llama una vez.

Como alternativa, puede reemplazar el método hijo y colocar el método padre en la instancia mientras llama al método hijo original.

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.