Méthodes de remplacement JavaScript

87

Disons que vous avez le code ci-dessous:

function A() {
    function modify() {
       x = 300;
       y = 400;
    }

    var c = new C();
}

function B() {
    function modify(){
       x = 3000;
       y = 4000;
    }

    var c = new C();
}

C = function () {
   var x = 10;
   var y = 20;

   function modify() {
      x = 30;
      y = 40;
   };

   modify();
   alert("The sum is: " + (x+y));
}

Maintenant , la question est, s'il y a une façon dont je peux passer outre la méthode modifyde Cles méthodes qui sont Aet B. En Java, vous utiliseriez le supermot clé, mais comment pouvez-vous réaliser quelque chose comme ça en JavaScript?

Daniel Nastase
la source
6
modifyn'est pas une méthode mais une fonction imbriquée - il y a une différence entre ces deux ...
Šime Vidas
2
En Java, vous utilisez le supermot - clé pour accéder aux champs et méthodes non privés d'une super-classe. Vous ne l'utilisez pas pour les remplacer.
FK82

Réponses:

138

Edit: Cela fait maintenant six ans que la réponse originale a été écrite et beaucoup de choses ont changé!

  • Si vous utilisez une version plus récente de JavaScript, éventuellement compilée avec un outil comme Babel , vous pouvez utiliser de vraies classes .
  • Si vous utilisez les constructeurs de composants de type classe fournis par Angular ou React , vous souhaiterez rechercher dans la documentation ce framework.
  • Si vous utilisez ES5 et créez de "fausses" classes à la main en utilisant des prototypes, la réponse ci-dessous est toujours aussi juste qu'elle ne l'a jamais été.

Bonne chance!


L'héritage JavaScript est un peu différent de Java. Voici à quoi ressemble le système d'objets JavaScript natif:

// Create a class
function Vehicle(color){
  this.color = color;
}

// Add an instance method
Vehicle.prototype.go = function(){
  return "Underway in " + this.color;
}

// Add a second class
function Car(color){
  this.color = color;
}

// And declare it is a subclass of the first
Car.prototype = new Vehicle();

// Override the instance method
Car.prototype.go = function(){
  return Vehicle.prototype.go.call(this) + " car"
}

// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"

var c = new Car("red");
c.go() // "Underway in red car"

Malheureusement, c'est un peu moche et cela n'inclut pas une très belle façon de "super": vous devez spécifier manuellement la méthode des classes parentes que vous voulez appeler. En conséquence, il existe une variété d'outils pour rendre la création de classes plus agréable. Essayez de regarder Prototype.js, Backbone.js ou une bibliothèque similaire qui inclut une syntaxe plus agréable pour faire la POO dans js.

Adam
la source
2
Alternativement à l'utilisation d'un outil pour «rendre la création de classes plus agréable», vous ne pouviez pas du tout créer de classes. L'émulation OO classique dans js devient toujours compliquée.
Raynos du
1
(commentaire non constructif) pour être le langage de "bas niveau" dans le monde des navigateurs est très laid sans raison. Je l'apprends encore, merci!
dnuske
9
Je crois qu'au lieu de Car.prototype = new Vehicle (); cela devrait être Car.prototype = Object.create (Vehicle.prototype); non?
Jordan
@Martin a raison, voir javascript-inheritance
matoni
Une des raisons pour lesquelles Car.prototype = new Vehicle (); est incorrect car il n'a rien à passer à Véhicule () pendant qu'il reçoit une couleur.
Tushar Arora
62

Puisqu'il s'agit d'un succès sur Google, j'aimerais donner une réponse mise à jour.

L' utilisation des classes ES6 facilite beaucoup l'héritage et le remplacement de méthode:

'use strict';

class A {
    speak() {
        console.log("I'm A");
    }
}

class B extends A {
    speak() {
        super.speak();

        console.log("I'm B");
    }
}

var a = new A();
a.speak();
// Output:
// I'm A

var b = new B();
b.speak();
// Output:
// I'm A
// I'm B

Le supermot-clé fait référence à la classe parente lorsqu'il est utilisé dans la classe héritière. De plus, toutes les méthodes de la classe parent sont liées à l'instance de l'enfant, vous n'avez donc pas besoin d'écrire super.method.apply(this);.

En ce qui concerne la compatibilité: le tableau de compatibilité ES6 ne montre que les versions les plus récentes des classes de support des principaux joueurs (principalement). Les navigateurs V8 les ont depuis janvier de cette année (Chrome et Opera), et Firefox, utilisant le moteur SpiderMonkey JS, verra des cours le mois prochain avec leur version officielle Firefox 45. Côté mobile, Android ne prend toujours pas en charge cette fonctionnalité, tandis qu'iOS 9, sorti il ​​y a cinq mois, est partiellement pris en charge.

Heureusement, il existe Babel , une bibliothèque JS pour recompiler le code Harmony en code ES5. Les classes et de nombreuses autres fonctionnalités intéressantes d'ES6 peuvent rendre votre code Javascript beaucoup plus lisible et maintenable.

Yotam Ofek
la source
10
Cette réponse mérite plus de crédit et est actuellement la bonne réponse.
Klompenrunner
6

Une fois devrait éviter d'émuler OO classique et utiliser OO prototypique à la place. Les traits sont une belle bibliothèque utilitaire pour OO prototypique .

Plutôt que d'écraser les méthodes et de configurer des chaînes d'héritage (il faut toujours privilégier la composition d'objets par rapport à l'héritage d'objets), vous devez regrouper les fonctions réutilisables en traits et créer des objets avec ceux-ci.

Exemple en direct

var modifyA = {
    modify: function() {
        this.x = 300;
        this.y = 400;
    }
};

var modifyB = {
    modify: function() {
        this.x = 3000;
        this.y = 4000;
    }
};

C = function(trait) {
    var o = Object.create(Object.prototype, Trait(trait));

    o.modify();
    console.log("sum : " + (o.x + o.y));

    return o;
}

//C(modifyA);
C(modifyB);
Raynos
la source
10
Vous ne répondez pas à la question. Cela devrait être un commentaire, le cas échéant.
FK82 du
3

modify () dans votre exemple est une fonction privée, qui ne sera accessible de nulle part que dans votre définition A, B ou C. Vous devrez le déclarer comme

this.modify = function(){}

C n'a pas de référence à ses parents, à moins que vous ne le passiez à C. Si C est configuré pour hériter de A ou B, il héritera de ses méthodes publiques (pas de ses fonctions privées comme vous avez défini modify ()). Une fois que C hérite des méthodes de son parent, vous pouvez remplacer les méthodes héritées.

Alex Heyd
la source
modifier est une fonction locale. Il n'y a pas de privé en javascript
Raynos
local / privé, n'est-ce pas la même chose, juste un terme différent?
Alex Heyd du
2

la méthode modify()que vous avez appelée dans le dernier est appelée dans un contexte global si vous souhaitez remplacer modify()vous devez d'abord hériter Aou B.

Peut-être que vous essayez de faire ceci:

Dans ce cas ChériteA

function A() {
    this.modify = function() {
        alert("in A");
    }
}

function B() {
    this.modify = function() {
        alert("in B");
    }
}

C = function() {
    this.modify = function() {
        alert("in C");
    };

    C.prototype.modify(); // you can call this method where you need to call modify of the parent class
}

C.prototype = new A();
amour
la source
1
C.prototype.modify()aurait la mauvaise thisvaleur. À savoir C.prototypeau lieu de l'instance de c. Veuillez utiliser, .call(this)mais votre réponse n'est qu'un double :)
Raynos
1

Sauf si vous rendez toutes les variables "publiques", c'est-à-dire en les rendant membres de Functionla prototypepropriété soit directement, soit via la propriété.

var C = function( ) {
    this.x = 10 , this.y = 20 ;
    this.modify = function( ) {
        this.x = 30 , this.y = 40 ;
        console.log("(!) C >> " + (this.x + this.y) ) ;
    } ;
} ;

var A = function( ) {
    this.modify = function( ) {
       this.x = 300 , this.y = 400 ;
       console.log("(!) A >> " + (this.x + this.y) ) ;
    } ;
} ;
    A.prototype = new C ;

var B = function( ) {
    this.modify = function( ) {
       this.x = 3000 , this.y = 4000 ;
       console.log("(!) B >> " + (this.x + this.y) ) ;
    } ;
} ;


new C( ).modify( ) ;
new A( ).modify( ) ;
new B( ).modify( ) ; 

Vous remarquerez quelques changements.

Plus important encore, l'appel au constructeur supposé "super-classes" est désormais implicite dans cette ligne:

<name>.prototype = new C ;

Les deux Aet Bauront désormais des membres individuellement modifiablesx et yce qui ne serait pas le cas si nous avions écrit à la ... = Cplace.

Ensuite, x, yet que modifytous les membres « publics » afin que l' attribution d' un autre Functionpour les

 <name>.prototype.modify = function( ) { /* ... */ }

"remplacera" l'original Functionpar ce nom.

Enfin, l'appel à modifyne peut pas être fait dans la Functiondéclaration car l'appel implicite à la "super-classe" serait alors à nouveau exécuté lorsque nous positionnerions la "super-classe" prototypesupposée à la propriété des "sous-classes" supposées.

Mais bon, c'est plus ou moins comment vous feriez ce genre de chose en JavaScript.

HTH,

FK

FK82
la source
Il ne sert à rien d' <name>.prototype = new C;observer tous les membres du prototype de toute façon
Raynos
@Raynos: Oui, il y en a. À savoir, tous les héritiers Objectspartageraient le même membre Csi vous n'instanciez pas un Cobjet. Ainsi, l' évolution xen Achangerait xdans Cet ainsi changer xen B. Ce qui est évidemment indésirable.
FK82 du
vous avez manqué le point. Vous pouvez supprimer la ligne et le code fonctionnerait toujours
Raynos
@Raynos: Vous manquez votre propre argument, j'en ai bien peur. ;-) Nous voulons Aet Bhériter de C. Si cette ligne manquait, ce ne serait pas le cas. En fait, le seul prototype à la fois Aainsi que Ble "shadow" aurait accès dans ce cas serait Object.prototype.
FK82 du
regardez le code. A et B n'utilisent aucun des membres de C dans le prototype. Donc "hériter" de C est inutile. En effet , Redéfinir A et B x, yet d' modifyombre et donc tous Cles membres « . Quel est l'intérêt de mettre C dans le prototype si vous ne l'utilisez pas? C'est du code mort.
Raynos
0

function A() {
    var c = new C();
	c.modify = function(){
		c.x = 123;
		c.y = 333;
	}
	c.sum();
}

function B() {
    var c = new C();
	c.modify = function(){
		c.x = 999;
		c.y = 333;
	}
	c.sum();
}


C = function () {
   this.x = 10;
   this.y = 20;

   this.modify = function() {
      this.x = 30;
      this.y = 40;
   };
   
   this.sum = function(){
	this.modify();
	console.log("The sum is: " + (this.x+this.y));
   }
}

A();
B();

nghien_net 89
la source