Méthode d'appel utilisant le prototype JavaScript


129

Est-il possible d'appeler la méthode de base à partir d'une méthode prototype en JavaScript si elle a été remplacée?

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

Quelle est la méthode de base? this.do = function () {} dans le constructeur?
Ionuț G. Stan

Réponses:


200

Je n'ai pas compris exactement ce que vous essayez de faire, mais normalement, la mise en œuvre d'un comportement spécifique à un objet se fait dans le sens suivant:

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
Je ne comprends pas, je suis venu ici exactement dans le même but que markvpc. Je veux dire, je veux avoir une sorte de méthode "privée" qui est utilisée à partir d'autres méthodes dans le prototype. Votre réponse m'oblige à créer une usine et j'aimerais savoir s'il existe un moyen d'éviter cela.
R01010010

10
N'utilisez pas de mots «Classe» dans JS car cela crée de la confusion. Il n'y a pas de cours en JavaScript
Polaris

1
Cela ne fonctionne que pour les objets instanciés. Que faire si vous souhaitez remplacer toutes les instances?
supertonsky du

Travaille pour moi! Exactement ce dont j'avais besoin. J'ai 4 constructeurs qui héritent d'un parent. Deux d'entre eux doivent remplacer et appeler une méthode sur le constructeur de base qui est leur prototype. Cela m'a permis exactement cela.
binarygiant

3
Je ne comprends pas pourquoi cela a autant de votes positifs et est marqué comme correct. Cela remplace uniquement le comportement d'une instance de la classe de base, pas toutes les instances de la sous-classe. Cela ne répond donc pas à la question.
BlueRaja - Danny Pflughoeft

24

Eh bien, une façon de le faire serait d'enregistrer la méthode de base, puis de l'appeler à partir de la méthode surchargée, comme ceci

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

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

        //do something new

    }else{
        return this._do_base();
    }

};

11
Cela créera une récursion infinie.
Johan Tidén

3
J'y suis allé: récursivité infinie.
jperelli

La clause else appelle et exécute _do_base, qui est une référence à prototype.do. Puisqu'aucun paramètre (ou état) n'a changé, le code ira à nouveau dans else, appelant à nouveau _do_base.
Johan Tidén

10
@ JohanTidén _do_basestocke une référence à la fonction définie auparavant. S'il n'y en avait pas, ce serait le cas undefined. JavaScript ne l'est pas - les makeexpressions ne sont jamais évaluées paresseusement comme vous le suggérez. Maintenant, si le prototype hérité de _do_base a également utilisé pour stocker ce qu'il pense être l'implémentation de son type de base do, alors vous avez un problème. Donc, oui, une fermeture serait un meilleur moyen, sûr pour l'héritage, de stocker la référence à l'implémentation de la fonction d'origine si vous utilisez cette méthode - bien que cela nécessite l'utilisation Function.prototype.call(this).
binki

16

J'ai bien peur que votre exemple ne fonctionne pas comme vous le pensez. Cette partie:

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

écrase la définition de

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

Étant donné que l'objet nouvellement créé a déjà une propriété "do", il ne recherche pas la chaîne prototypique.

La forme classique d'héritage en Javascript est maladroite et difficile à saisir. Je suggérerais plutôt d'utiliser le modèle d'héritage simple de Douglas Crockford. Comme ça:

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

À mon avis, une manière beaucoup plus claire de gérer les objets, les constructeurs et l'héritage en javascript. Pour en savoir plus, consultez Crockfords Javascript: The good parts .


3
Très bien en effet, mais vous ne pouvez pas vraiment appeler en base_dotant que fonction, car vous perdez ainsi toute thisliaison dans la dométhode d' origine . Ainsi, la configuration de la méthode de base est un peu plus complexe, surtout si vous souhaitez l'appeler en utilisant l'objet de base au thislieu de l'objet enfant. Je suggérerais quelque chose qui ressemble à base_do.apply(me, arguments).
Giulio Piancastelli

Il suffit de se demander, pourquoi utilisez-vous pas varpour base_do? Est-ce exprès et, si oui, pourquoi? Et, comme l'a dit @GiulioPiancastelli, cette rupture de thisliaison à l'intérieur base_do()ou cet appel héritera-t-il le thisde son appelant?
binki

@binki J'ai mis à jour l'exemple. Le vardevrait être là. Utiliser call(ou apply) nous permet de lier thiscorrectement.
Magnar

10

Je sais que cet article date d'il y a 4 ans, mais à cause de mon expérience en C #, je cherchais un moyen d'appeler la classe de base sans avoir à spécifier le nom de la classe, mais plutôt l'obtenir par une propriété de la sous-classe. Donc mon seul changement à la réponse de Christoph serait

De ceci:

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

Pour ça:

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

6
Ne faites pas cela, this.constructorne le pointez pas toujours MyClass.
Bergi

Eh bien, ça devrait, à moins que quelqu'un ne se trompe en créant l'héritage. 'this' doit toujours faire référence à l'objet de niveau supérieur, et sa propriété 'constructor' doit toujours pointer vers sa propre fonction constructeur.
Triynko

5

si vous définissez une fonction comme celle-ci (en utilisant la POO)

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

il y a deux façons d'appeler une fonction prototype: 1) créer une instance et appeler la fonction objet:

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

et l'autre façon est ... 2) appelle la fonction directement à partir du prototype:

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

5

Cette solution utilise Object.getPrototypeOf

TestA est super qui a getName

TestBest un enfant qui remplace getNamemais, a aussi getBothNamesqui appelle la superversion de getNameainsi que la childversion

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

Une alternative :

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

Malgré cela, je ne recommanderai pas cette solution. Il y a de nombreuses raisons pour lesquelles l'utilisation __proto__(qui modifie le [[Prototype]] `d'un objet) doit être évitée. Veuillez utiliser la Object.create(...)route à la place.
KarelG

2

Si je comprends bien, vous voulez que la fonctionnalité de base soit toujours exécutée, alors qu'une partie de celle-ci devrait être laissée aux implémentations.

Vous pourriez être aidé par le modèle de conception « méthode modèle ».

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

Si vous connaissez votre super classe par son nom, vous pouvez faire quelque chose comme ceci:

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

Cela imprimera

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

comme prévu.


1

Une autre façon avec ES5 est de parcourir explicitement la chaîne de prototypes en utilisant 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

Non, vous devrez donner à la fonction do dans le constructeur et à la fonction do dans le prototype des noms différents.


0

De plus, si vous souhaitez remplacer toutes les instances et pas seulement cette instance spéciale, celle-ci peut vous aider.

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

résultat:

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();
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.