Chamando o método usando o protótipo JavaScript


129

É possível chamar o método base a partir de um método protótipo em JavaScript, se ele tiver sido substituído?

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

Qual é o método base? this.do = function () {} no construtor?
Ionuţ G. Stan

Respostas:


200

Eu não entendi o que exatamente você está tentando fazer, mas normalmente a implementação de comportamento específico de objeto é feita nessas linhas:

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
Eu não entendi isso, eu vim aqui exatamente para o mesmo propósito que o markvpc. Quero dizer, eu quero ter um tipo de método "privado" que é usado a partir de outros métodos no protótipo. Sua resposta me obriga a criar uma fábrica e eu gostaria de saber se há alguma maneira de evitar isso.
R01010010

10
Não use palavras "Class" em JS, pois isso cria confusão. Não há aulas em JavaScript
Polaris

1
Isso funciona apenas para objetos instanciados. E se você quiser substituir todas as instâncias?
Supertonsky #

Funciona para mim! Exatamente o que eu precisava. Eu tenho 4 construtores que herdam de um pai. Dois deles precisam substituir e chamar um método no construtor de base que é seu protótipo. Isso me permitiu exatamente isso.
binarygiant

3
Não entendo por que isso tem tantos votos positivos e está marcado como correto. Isso substitui apenas o comportamento de uma instância da classe base, nem todas as instâncias da subclasse. Portanto, isso não responde à pergunta.
BlueRaja - Danny Pflughoeft

24

Bem, uma maneira de fazer isso seria salvar o método base e depois chamá-lo a partir do método substituído, assim

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

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

        //do something new

    }else{
        return this._do_base();
    }

};

11
Isso criará uma recursão infinita.
Johan Tidén

3
Eu já estive lá: recursão infinita.
jperelli

A cláusula else está chamando e executando _do_base, que é uma referência ao prototype.do. Como nenhum parâmetro (ou estado) foi alterado, o código entrará novamente no outro, chamando _do_base novamente.
Johan Tidén

10
@ JohanTidén _do_basearmazena uma referência a qualquer função que foi definida anteriormente. Se não houvesse, então seria undefined. O JavaScript não é - as makeexpressões nunca são avaliadas preguiçosamente como você sugere. Agora, se o protótipo herdado de também usou _do_base para armazenar o que ele acha que é a implementação de seu tipo base do, você tem um problema. Portanto, sim, um fechamento seria uma maneira melhor e segura de herança para armazenar a referência à implementação da função original se você usar esse método - embora isso exija o uso Function.prototype.call(this).
binki

16

Receio que seu exemplo não funcione da maneira que você pensa. Esta parte:

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

substitui a definição de

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

Como o objeto recém-criado já possui uma propriedade "do", ele não pesquisa a cadeia de protótipos.

A forma clássica de herança em Javascript é difícil e difícil de entender. Eu sugeriria usar o padrão simples de herança de Douglas Crockfords. Como isso:

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

Na minha opinião, uma maneira muito mais clara de lidar com objetos, construtores e herança em javascript. Você pode ler mais em Crockfords Javascript: The good parts .


3
Muito bom mesmo, mas você não pode realmente chamar base_docomo uma função, porque você perde qualquer thisligação no dométodo original dessa maneira. Portanto, a configuração do método base é um pouco mais complexa, especialmente se você quiser chamá-lo usando o objeto base como em thisvez do objeto filho. Eu sugeriria algo parecido base_do.apply(me, arguments).
Giulio Piancastelli

Basta saber, por que você não use varpara base_do? Isso é de propósito e, se sim, por quê? E, como disse o GiulioPiancastelli, isso quebra a thisligação base_do()ou a chamada herdará a thisdo chamador?
binki

@binki Atualizei o exemplo. O vardeveria estar lá. Usar call(ou apply) nos permite vincular thiscorretamente.
Magnar

10

Eu sei que este post é de 4 anos atrás, mas por causa do meu background em C #, eu estava procurando uma maneira de chamar a classe base sem precisar especificar o nome da classe, mas obtê-lo por uma propriedade na subclasse. Então, minha única mudança na resposta de Christoph seria

Disto:

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

Para isso:

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

6
Não faça isso, this.constructornem sempre pode apontar MyClass.
Bergi

Bem, deveria, a menos que alguém estragasse a criação da herança. 'this' deve sempre se referir ao objeto de nível superior e sua propriedade 'constructor' sempre deve apontar para sua própria função construtora.
Triynko 23/02

5

se você definir uma função como esta (usando OOP)

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

existem duas maneiras de chamar uma função de protótipo: 1) criar uma instância e chamar a função de objeto:

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

e a outra maneira é ... 2) está chamando a função diretamente do protótipo:

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

5

Esta solução usa Object.getPrototypeOf

TestA é super que tem getName

TestBé um filho que substitui getName, mas que também getBothNameschama a superversão getNamee a childversão

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

Uma alternativa :

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

Apesar de funcionar, não recomendo esta solução. Existem muitas razões pelas quais o uso __proto__(que altera o [[Protótipo]] `de um objeto) deve ser evitado. Por favor, use a Object.create(...)rota.
KarelG

2

Se bem entendi, você deseja que a funcionalidade Base seja sempre executada, enquanto uma parte dela deve ser deixada para implementações.

Você pode ser ajudado pelo padrão de design do ' método de modelo '.

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

Se você conhece sua super turma pelo nome, pode fazer algo assim:

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

Isso imprimirá

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

como esperado.


1

Outra maneira com o ES5 é atravessar explicitamente a cadeia de protótipos usando 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

Não, você precisaria dar a função do no construtor e a função do nos nomes diferentes do protótipo.


0

Além disso, se você deseja substituir todas as instâncias e não apenas uma instância especial, essa pode ajudar.

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

resultado:

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();
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.