Como chamar um método pai da classe filho em javascript?

156

Passei as últimas duas horas tentando encontrar uma solução para o meu problema, mas parece sem esperança.

Basicamente, preciso saber como chamar um método pai de uma classe filho. Todas as coisas que eu tentei até agora acabam não funcionando ou substituindo o método pai.

Estou usando o seguinte código para configurar o OOP em 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);

Preciso chamar o método dos pais primeiro e depois adicionar mais algumas coisas na classe filho.

Na maioria das linguagens OOP, isso seria tão simples quanto chamar. parent.myMethod() Mas eu realmente não consigo entender como isso é feito em javascript.

Qualquer ajuda é muito apreciada, obrigado!

YemSalat
fonte

Respostas:

196

Aqui está como é feito: ParentClass.prototype.myMethod();

Ou, se você quiser chamá-lo no contexto da instância atual, poderá: ParentClass.prototype.myMethod.call(this)

O mesmo vale para chamar um método pai da classe filho com argumentos: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)* Dica: use em apply()vez de call()passar argumentos como uma matriz.

YemSalat
fonte
7
Se você quiser chamá-lo no contexto da instância atual, precisará fazer ParentClass.prototype.myMethod.apply() or ParentClass.prototype.myMethod.call () `, como faz com seu construtor.
JMM
3
Basta adicionar em que, se você quiser chamar com argumentos, eles vão dentro da aplicar ou função de chamada ( ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);)
Gershom
Eu não entendo Se eu chamar ParentClass.prototype.myMethod.call (this); do myMethod de ChildObject, recebi um erro "TypeError não capturado: não é possível ler a propriedade 'call' de undefined".
zhekaus
@zhekaus, isso significa que você não tem myMethodaula.
YemSalat
2
Atualmente, estou usando this.myFun = function () {} para declarar um método de objeto, então chame ParentClass.prototype.myFun.call (...) não está funcionando, então eu tenho que usar CurrentClass.prototype.myFun.call ( ...) JS é ... uma porcaria, devemos usar OOP real.
Loenix
156

O estilo ES6 permite usar novos recursos, como superpalavra-chave. superA palavra-chave trata-se do contexto da classe pai, quando você usa a sintaxe das classes ES6. Como um exemplo muito simples, faça o checkout:

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

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

Além disso, você pode usar superpara chamar o construtor pai:

class Foo {}

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

E é claro que você pode usá-lo para acessar as propriedades da classe pai super.prop. Então, use o ES6 e seja feliz.

Dmytro Medvid
fonte
10
@ fsinisi90 Creio que a questão não é sobre os métodos de classe dos pais, mas sobre os métodos de instância dos pais que simplesmente não podem ser chamados com super palavra-chave a partir do ES6.
mcmlxxxiii
ele funciona também para métodos que não são estáticos (testado com o Chrome, sem transpiliing, não tentei a palavra-chave estática)
Gianluca Casati
Por que ele precisa superser chamado? Existe uma equivalência no JS "antigo"?
1252748
3
super () deve ser chamado no construtor de classe filho antes de qualquer outra coisa.
User938363
1
@ GianlucaCasati: você só pode usar super()em métodos estáticos; parece que você o usou no construtor.
ZzZombo
5

Bem, para fazer isso, você não está limitado à Classabstração do ES6. O acesso aos métodos de protótipo do construtor pai é possível por meio da __proto__propriedade (tenho certeza de que haverá outros codificadores JS para reclamar que estão depreciados), o que está depreciado, mas ao mesmo tempo descobriu que é realmente uma ferramenta essencial para as necessidades de subclasses ( especialmente para as necessidades de subclassificação de matriz). Portanto, embora a __proto__propriedade ainda esteja disponível em todos os principais mecanismos JS que eu conheço, o ES6 introduziu a Object.getPrototypeOf()funcionalidade em cima dela. osuper() ferramenta noClass abstração é um açúcar sintático disso.

Portanto, caso você não tenha acesso ao nome do construtor pai e não queira usar a Classabstração, você ainda pode fazer o seguinte;

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);
    }
}
Redu
fonte
4

No caso de múltiplos níveis de herança, essa função pode ser usada como um método super () em outros idiomas. Aqui está um violino demo , com alguns testes, você pode usá-lo assim, dentro do seu método use:call_base(this, 'method_name', arguments);

Utiliza funções ES muito recentes, não é garantida uma compatibilidade com navegadores mais antigos. Testado em 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
fonte
2

Que tal algo baseado na ideia 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
Alessandro
fonte
2

Aqui está uma boa maneira de os objetos filhos terem acesso às propriedades e métodos pai usando a cadeia de protótipos do JavaScript e é compatível com o Internet Explorer. O JavaScript pesquisa métodos na cadeia de protótipos e queremos que a cadeia de protótipos da criança tenha a seguinte aparência:

Instância filho -> Protótipo do filho (com métodos filho) -> Protótipo do pai (com métodos pai) -> Protótipo de objeto -> nulo

Os métodos filho também podem chamar métodos pai sombreados, conforme mostrado nos três asteriscos *** abaixo.

Aqui está como:

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

Existe uma solução muito mais fácil e compacta para a pesquisa de protótipos em vários níveis, mas requer Proxysuporte. Uso: SUPER(<instance>).<method>(<args>), por exemplo, assumindo que duas classes Ae B extends Acom método 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
fonte
0

Enquanto você pode chamar o método pai pelo protótipo do pai, você terá de passar a instância criança atual para usar call, applyou bindmétodo. obind método criará uma nova função, portanto, não recomendo que se você se preocupa com o desempenho, exceto se ele for chamado apenas uma vez.

Como alternativa, você pode substituir o método filho e colocar o método pai na instância enquanto chama o método filho 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'));

StefansArya
fonte