2009-02-18 20 views
115

¿Es posible llamar al método base desde un método prototipo en JavaScript si se ha redefinido?Método de llamada usando el prototipo JavaScript

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

¿Cuál es el método de base? this.do = function() {} en el constructor? –

Respuesta

190

no entendía exactamente lo que estamos tratando de hacer, pero normalmente la aplicación de un comportamiento específico a objetos que se hace a lo largo de estas líneas:

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

No entiendo esto, vine aquí exactamente con el mismo propósito que markvpc. Quiero decir, quiero tener un tipo de método "privado" que se usa desde otros métodos en el prototipo. Tu respuesta me obliga a crear una fábrica y me gustaría saber si hay alguna forma de evitar eso. – R01010010

+9

No use palabras "Clase" en JS porque crea confusión. No hay clases en JavaScript – Polaris

+1

Esto solo funciona para objetos instanciados. ¿Qué sucede si desea anular todas las instancias? – supertonsky

2

Si he entendido bien, desea la funcionalidad de la base siempre se realizará, mientras que una parte de ella se debe dejar a las implementaciones.

Es posible que obtenga ayuda con el patrón de diseño 'template method'.

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 */ } 
25

Bueno, una forma de hacerlo sería ahorro el método de base y luego llamando desde el método sobreescrito, al igual que

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

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

     //do something new 

    }else{ 
     return this._do_base(); 
    } 

}; 
+10

Esto creará infinitas recursiones. –

+3

He estado allí: recursión infinita. – jperelli

+1

@ JohanTidén ¿cómo? – binki

15

Me temo que su ejemplo no funciona la forma de pensar . Esta parte:

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

sobrescribe la definición de

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

Puesto que el objeto recién creado ya tiene un "hacer" la propiedad, que no se ve en la cadena de prototipos.

La forma clásica de herencia en Javascript es incómoda y difícil de entender. Sugeriría utilizar el patrón de herencia simple Douglas Crockfords en su lugar. De esta manera:

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 

En mi opinión una manera mucho más clara de la manipulación de objetos, constructores y herencia en JavaScript. Puede leer más en Crockfords Javascript: The good parts.

+3

Muy bien, pero no se puede llamar 'base_do' como una función, ya que se pierde cualquier enlace' this' en el método 'do' original de esa manera. Por lo tanto, la configuración del método base es un poco más compleja, especialmente si desea llamarlo utilizando el objeto base como 'this' en lugar del objeto secundario. Sugeriría algo parecido a 'base_do.apply (me, arguments)'. –

+0

Solo me pregunto, ¿por qué no usas 'var' para' base_do'? ¿Es esto a propósito y, de ser así, por qué? Y, como dijo @GiulioPiancastelli, ¿esto rompe 'esto' vinculante dentro de' base_do() 'o esa llamada heredará' this' de su llamador? – binki

+0

@binki He actualizado el ejemplo. La 'var' debería estar allí. Usar 'call' (o' apply') nos permite unir 'this' correctamente. – Magnar

0

No, necesitaría dar la función do en el constructor y la función do en el prototipo diferentes nombres.

10

Sé que esta publicación es de hace 4 años, pero debido a mi historial de C# estaba buscando una forma de llamar a la clase base sin tener que especificar el nombre de la clase, sino obtenerla por una propiedad en la subclase. Así que mi único cambio en Christoph's answer sería

De esta:

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

A esto:

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

No haga esto, 'this.constructor' podría no apuntar siempre a' MyClass'. – Bergi

+0

Bueno, debería hacerlo, a menos que alguien arruine la configuración de la herencia. 'this' siempre debe referirse al objeto de nivel superior, y su propiedad 'constructor' siempre debe apuntar a su propia función de constructor. – Triynko

3
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

si se define una función como esta (usando programación orientada a objetos)

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

allí i s dos formas de llamar a una función prototipo: 1) hacer una instancia y llamar a la función del objeto:

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

y la otra forma es ...2) se llama a la función directamente del prototipo:

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

Si sabe que su super clase por su nombre, se puede hacer algo como esto:

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

Esto imprimirá

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

como se esperaba.

0

Además, si desea anular todas las instancias y no solo esa instancia especial, esta podría ayudar.

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

Una 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); 
} 
+0

A pesar de que puede funcionar, no recomendaré esta solución. Hay muchas razones por las que se usa ['__proto__'] (https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) (que cambia el [[Prototype]]' de objeto) debe ser evitado. Utilice la ruta ['Object.create (...)'] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) en su lugar. – KarelG

4
 
Following is the solution that worked for me. It uses Object.getPrototypeOf. 
TestA is super that has getName 
TestB is a child that overrides getName but, also has 
getBothNames that calls the super version of getName as well as the child version 

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

1

Otra forma con ES5 es atravesar explícitamente la cadena de prototipos utilizando 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() 
Cuestiones relacionadas