web-dev-qa-db-fra.com

Méthodes de substitution JavaScript

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 de savoir s'il existe un moyen de remplacer la méthode modify de C par les méthodes en A et B. Le mot-clé super en Java serait utilisé, mais comment réaliser quelque chose comme cela en JavaScript?

67
Daniel Nastase

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 tel que 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 ce framework dans les docs.
  • Si vous utilisez ES5 et créez des classes "factices" à la main à l'aide de prototypes, la réponse ci-dessous est toujours la même. 

Bonne chance!


L'héritage JavaScript est un peu différent de Java. Voici à quoi ressemble le système d'objet 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 manière très agréable de "super": vous devez spécifier manuellement la méthode de la classe parente 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 consulter Prototype.js, Backbone.js ou une bibliothèque similaire qui inclut une syntaxe plus agréable pour exécuter OOP dans js.

120
Adam

Comme il s'agit d'un succès sur Google, je voudrais donner une réponse mise à jour.

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

'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 mot clé super fait référence à la classe parente lorsqu'elle est utilisée dans la classe d'héritage. De plus, toutes les méthodes de la classe parent sont liées à l'instance de l'enfant. Vous n'avez donc pas à écrire super.method.apply(this);.

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

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

28
Yotam Ofek

Once devrait éviter d’émuler le classique OO et utiliser plutôt le prototype OO. Une bibliothèque d’utilitaires de Nice pour le prototype OO est traits .

Plutôt que de remplacer les méthodes et de configurer les chaînes d'héritage (il faut toujours privilégier la composition d'objet plutôt que l'héritage d'objet), vous devez regrouper des fonctions réutilisables dans des 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);
6
Raynos

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

this.modify = function(){}

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

3
Alex Heyd

la méthode modify() que vous avez appelée en dernier lieu est appelée dans un contexte global si vous souhaitez remplacer modify(), vous devez d'abord hériter de A ou B 

Peut-être que vous essayez de faire ceci:

Dans ce cas, C hérite de A

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

Non, sauf si vous rendez toutes les variables "publiques", c’est-à-dire, faites-en des membres de la variable Function directement ou par le biais de la propriété prototype.

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 maintenant implicite dans cette ligne:

<name>.prototype = new C ;

A et B auront désormais individuellement modifiables membres x et y, ce qui ne serait pas le cas si nous aurions écrit ... = C à la place.

Alors, x, y et modify sont tous des membres "publics" de sorte que vous leur affectez une Function différente

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

"écrasera" l'original Function par ce nom.

Enfin, l'appel à modify ne peut pas être effectué dans la déclaration Function car l'appel implicite à la "super-classe" serait alors exécuté à nouveau lorsque nous définirions la "super-classe" supposée sur la propriété prototype des supposées "sous-classes ".

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

HTH,

FK

1
FK82

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

0
nghien_net 89