web-dev-qa-db-fra.com

Javascript redéfinit et annule le corps de la fonction existante

Je me demande si nous pouvons toujours changer le corps de fonction une fois qu'il est construit. 

     var O = function(someValue){
           this.hello = function(){
                return "hello, " + someValue;
           }
     }

     O.prototype.hello = function(){
           return "hhhhhhh";
     }

     var i = new O("chris");
     i.hello();   // -> this still returns the old definition "hello, chris"

L'instruction javascript O.prototype.hello = function(){....} ne remplace pas et ne redéfinit pas le comportement de la fonction hello. Pourquoi donc ? Je sais qu'il y aura une erreur de type si vous essayez de réutiliser le paramètre someValue.

      // this will fail since it can't find the parameter 'someValue'
      O.prototype.hello = function(){
             return "aloha, " + someValue;
      } 

Je me demande pourquoi cela permet d’ajouter des fonctions pendant l’exécution, comme

      O.prototype.newFunction = function(){
           return "this is a new function";
      }

      i.newFunction();   //  print 'this is a new function' with no problem.

mais ne vous permet pas de changer la définition une fois qu'elle est définie .. Est-ce que j'ai fait quelque chose de mal? comment remplacer et redéfinir une fonction dans une classe? et existe-t-il un moyen de réutiliser le paramètre que nous avons transmis précédemment pour créer l'objet? dans ce cas, comment pouvons-nous réutiliser someValue si nous voulons y étendre davantage de fonctions.

22
peter

Lorsque vous utilisez new, la valeur de this dans le constructeur pointe vers le nouvel objet créé (pour plus d'informations sur le fonctionnement de new, consultez cette réponse et cette réponse ). Votre nouvelle instance i a donc une fonction hello. Lorsque vous essayez d'accéder à la propriété d'un objet, il parcourt la chaîne de prototypes jusqu'à ce qu'il le trouve. Comme hello existe sur l'instance de l'objet, il n'est pas nécessaire de parcourir la chaîne de prototypes pour accéder à la version de hello qui renvoie hhhhhhhh. En un sens, vous avez remplacé l'implémentation par défaut dans votre instance.

Vous pouvez voir ce comportement si vous n'affectez pas hello à this dans votre constructeur:

var O = function(someValue) {

 }

 O.prototype.hello = function(){
       return "hhhhhhh";
 }

 var i = new O("chris");
 console.log(i.hello()); //this prints out hhhhhhh

Ce que vous faites est un peu en arrière. Le prototype fournit essentiellement la forme "par défaut" de quelque chose, que vous pouvez remplacer au cas par cas. Le formulaire par défaut est utilisé uniquement si la propriété que vous recherchez ne peut pas être trouvée sur l'objet. C'est-à-dire que JavaScript commencera à remonter la chaîne de prototypes pour voir s'il peut trouver une propriété qui correspond à ce que vous recherchez. S'il le trouve, il l'utilisera. Sinon, il retournera undefined.

Ce que vous avez fondamentalement dans le premier cas est le suivant:

Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
     |
     +----i.hello (returns "hello, chris")

Ainsi, lorsque vous exécutez i.hello, JavaScript constate qu'il existe une propriété hello sur i et l'utilise. Maintenant, si vous n'avez pas explicitement défini une propriété hello, vous avez essentiellement les éléments suivants:

Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
     |
     +----i.hello (is "undefined", so JavaScript will walk up the chain until 
                   it sees O.prototype.hello, which does have a defined value 
                   it can use.)

Cela signifie que vous pouvez fournir une implémentation par défaut dans le prototype, puis la remplacer (dans un sens, c'est comme une sous-classification). Ce que vous pouvez également faire est de modifier le comportement instance par instance en modifiant directement l’instance. La version de hello que vous avez sur le prototype est en quelque sorte une solution de secours et une solution de secours.

EDIT: Réponses à vos questions:

Le remplacement par instance signifie que vous associez une propriété ou une fonction à une instance particulière. Par exemple, vous pourriez faire:

i.goodbye = function() {
    return "Goodbye, cruel world!";
};

Ce qui signifie que ce comportement est spécifique à cette instance particulière (c'est-à-dire uniquement à i et à aucune autre instance que vous avez créée).

Si vous supprimez this, vous avez essentiellement:

hello = function() {
    return "hello, " + someValue;
}

Ce qui équivaut à faire:

window.hello = function() {
    return "hello, " + someValue;
}

Donc, dans ce cas, hello est une référence globale à cette fonction. Cela signifie que hello n'est associé à aucun de vos objets.

hello peut être indéfini si vous n'avez pas this.hello = function() { .... }; dans votre constructeur. Je parlais également du processus général utilisé par JavaScript pour tenter de résoudre les propriétés des objets. Comme je l'ai déjà mentionné, il s'agit de remonter la chaîne de prototypes.

23
Vivin Paliath

Lorsque vous créez une instance de l'objet O à l'aide de new O("somename");, vous affectez une méthode instance au nouvel objet créé. Lorsque vous affectez ensuite une autre méthode du même nom à O 's prototype, la méthode est déjà occultée par la méthode d'instance. Alors:

Object.prototype.hello // undefined
       |
       O.prototype.hello // alternate function
         |
         i.hello // original function provided in constructor

JavaScript commence au bas de la chaîne et s'arrête lorsqu'il trouve une correspondance pour le nom. Donc, il s'arrête à i.hello et ne voit jamais O.prototype.hello.

JavaScript (à partir de ECMAScript 5) ne vous donne vraiment pas (pour autant que je sache) un bon moyen de créer des variables privées comme celle-ci, accessibles aux méthodes d'instance ajoutées après la définition (soit ajouté on l'instance ou sur le prototype). Les fermetures vous font presque tout le chemin, mais si vous voulez pouvoir ajouter des méthodes en dehors de la fermeture qui ont accès aux variables de fermeture, vous devez exposer les méthodes get et/ou set qui donnent à ces nouvelles méthodes l'accès aux variables de fermeture:

// Possibility #1 - marked as private member
var O = function(someValue) {
    this._someValue = someValue;
};
O.prototype.hello = function() { return "hhhh"; };

var i = new O("somename");
i.hello = function() { return "aloha," + this._someValue; };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

// Possibility #2 - factory function + closure with get and set methods
var OMaker = function(someValue) {
    var realO = function() {};
    realO.prototype.getSomeValue = function() { return someValue; };
    realO.prototype.setSomeValue = function(newVal) { someValue = newVal; };
    realO.prototype.hello = function() { return "hhhh"; };
    return realO;
};
var O = OMaker("somename"),
            i = new O();
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

// Possibility #3 - eschew prototype inheritance and create new objects
var O = function(someValue) {
    return {
        getValue: function() { return someValue; },
        setValue: function(newValue) { someValue = newValue; },
        hello: function() { return "hhhh"; }
    };
};
var i = O(); // Note the lack of the "new" keyword
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

Vous voudrez vraiment lire la réponse géniale de bobince sur OOP en JavaScript pour plus d'informations sur le sujet.

3
Sean Vieira

Tout d'abord, vous devez comprendre l'héritage prototypal.

Lorsque vous créez un objet en utilisant O en tant que constructeur, ceci se produit:

  • 1 °, un nouvel objet est créé.
  • Deuxièmement, la propriété hello de cet objet est définie sur une fonction (via le constructeur que vous avez défini).
  • 3ème, un lien secret de l'objet, pointant vers l'objet O.prototype, est créé.

Lors de la référence à des propriétés d'objets O, ces propriétés sont d'abord recherchées dans l'objet même. Ce n'est que si l'objet n'a pas la propriété elle-même qu'il se conforme à son prototype.

Deuxièmement, vous devez comprendre les fermetures.

someValue est une variable (pas une propriété) définie dans la fonction O. On ne peut y accéder que par d'autres éléments également définis dans la même fonction (ou par toute fonction définie dans la fonction O). Donc, nous disons "someValue était fermé sur". Une fonction que vous définissez en dehors de O ne peut y accéder.


Pour obtenir ce que vous voulez, vous devez soit affecter une valeur à une propriété (ce qui la rend moins semblable à une chose private et davantage à une chose public). Ou bien, vous devez définir toutes les fonctions qui nécessitent un accès à someValue à l'intérieur de la définition d'origine de O.

Pour changer ce que i.hello pointe après la création de la i, vous devez définir directement la propriété de l'objet.

i.hello = function () { /* stuff */ };

1
MicronXD

Non, vous ne pouvez pas, mais voici un bon exemple de moyens de contourner cette limitation en structurant votre héritage de manière différente.

Méthodes de substitution JavaScript

// 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"
1

Lorsque vous accédez à une propriété, le système la recherche d'abord dans l'instance. S'il n'est pas trouvé, il le cherche dans le prototype. C'est pourquoi this.hello est utilisé plutôt que O.prototype.hello.

Si vous souhaitez remplacer l'implémentation de hello, vous devez utiliser l'héritage JavaScript. Voici un exemple de base:

var A = function(){
    console.log("A is getting constructed");
};

A.prototype.constructor = A;
A.prototype.someValue = 1;
A.prototype.hello = function() {
    console.log("A.hello(): " + this.someValue);
};

var B = function(){
    //Constructor of A is automatically called before B's

    console.log("B is getting constructed");
};
B.prototype = new A; //Inherit from A
B.prototype.constructor = B;
B.prototype.hello = function() {
    console.log("B.hello() called");
    console.log("Calling base class method");
    A.prototype.hello.call(this);
};

var a = new A();
a.hello();

var b = new B();
b.hello();
0
RajV

Méthode de remplacement refreshEditor pour l'instance créée:

var cp = hot1.getPlugin('comments');
cp.refreshEditor = (function (original) {
  return function (force) {
    //do something additional
    if(console) {
      console.log('test!!!!!!!!!');
      console.log(force)
    }
    original.call(cp, force);
  }
})(cp.refreshEditor);
0
SmaLL

Si je me souviens bien, les fonctions qui sont des membres directs des objets sont prioritaires par rapport aux membres du prototype de cet objet portant le même nom. Par conséquent, O.prototype.hello est usurpé par O.hello, même si le premier est défini plus tard dans le code.

La raison pour laquelle someValue n'est pas disponible pour votre O.prototype.hello est que la portée de someValue est limitée à la fonction constructeur et à toutes les fonctions définies ou exécutées dans celle-ci. Puisque O.prototype.hello est défini en dehors de la portée du constructeur O, il ne sait pas que someValue

0
jackwanders