Quand utiliser la programmation prototypique en JavaScript

15

J'ai passé beaucoup de temps à développer des widgets simples pour les projets de la manière suivante:

var project = project || {};

(function() {

  project.elements = {
    prop1: val1,
    prop2: val2
  }

  project.method1 = function(val) {
    // Do this
  }

  project.method2 = function(val) {
    // Do that
  }

  project.init = function() {
    project.method1(project.elements.prop1)
    project.method2(project.elements.prop2)
  }
})()

project.init();

Mais j'ai commencé à changer mon format comme suit:

function Project() {
  this.elements = {
    prop1: val1,
    prop2: val2
  }

  this.method_one(this.elements.prop1);
  this.method_two(this.elements.prop2);
}

Project.prototype.method_one = function (val) {
  // 
};

Project.prototype.method_two = function (val) {
  //
};

new Project();

Certes, ce sont des exemples stupides, alors ne vous enroulez pas autour de l'axe. Mais quelle est la différence fonctionnelle et quand dois-je choisir l'un ou l'autre?

JDillon522
la source
Je pense que votre premier exemple de code ne fonctionnera pas car le projet ne quitte pas la portée. Pour utiliser l'héritage dans JS, vous utilisez le prototypage. Le premier exemple n'est pas destiné à étendre le projet, la réutilisabilité peut donc être limitée.
Aitch
Ouais, j'ai accidentellement mis la projectdéclaration dans la fonction. Mis à jour.
JDillon522
pareil ici. le premier exemple est statique, le second est orienté objet.
Aitch
1
Si vous n'utilisez qu'une seule instance d'objet, il n'y a aucune raison d'utiliser la définition prototypique. Et ce dont vous semblez avoir besoin - c'est un format de module - il y a beaucoup d'options pour cela, voir: addyosmani.com/writing-modular-js
c69
1
Premier pour le modèle singleton Le second pour les non-singleton (quand 1 classe peut avoir beaucoup d'objets)
Kokizzu

Réponses:

6

La première différence peut être résumée comme suit: thisfait référence à l' instance de la classe. prototypefait référence à la définition .

Disons que nous avons la classe suivante:

var Flight = function ( number ) { this.number = number; };

Donc, ici, nous nous attachons this.numberà chaque instance de la classe, et cela a du sens car chacun Flightdevrait avoir son propre numéro de vol.

var flightOne = new Flight( "ABC" );
var flightTwo = new Flight( "XYZ" );

En revanche, prototypedéfinit une propriété unique accessible à toutes les instances.

Maintenant, si nous voulons obtenir le numéro de vol, nous pouvons simplement écrire l'extrait de code suivant et toutes nos instances obtiendront une référence à ce nouvel objet prototypé.

Flight.prototype.getNumber = function () { return this.number; };

La deuxième différence concerne la façon dont JavaScript recherche une propriété d'un objet. Lorsque vous recherchez Object.whatever, JavaScript va jusqu'à l' objet Object principal (l'objet dont tout le reste a hérité), et dès qu'il trouvera une correspondance, il retournera ou l'appellera.

Mais cela ne se produit que pour les propriétés prototypées. Donc, si vous avez quelque part dans les niveaux supérieurs this.whatever, JavaScript ne le considérera pas comme une correspondance et poursuivra la recherche.

Voyons comment cela se passe en réalité.

Notez d'abord que [presque] tout est des objets en JavaScript. Essaye ça:

typeof null

Voyons maintenant ce qu'il y a à l'intérieur Object(notez les majuscules Oet .à la fin). Dans les outils de développement de Google Chrome, lorsque vous entrez, .vous obtiendrez une liste des propriétés disponibles à l'intérieur de cet objet spécifique.

Object.

Faites maintenant la même chose pour Function:

Function.

Vous remarquerez peut-être la nameméthode. Allez-y et lancez-le et voyons ce qui se passe:

Object.name
Function.name

Créons maintenant une fonction:

var myFunc = function () {};

Et voyons si nous avons aussi la nameméthode ici:

myFunc.name

Vous devriez obtenir une chaîne vide, mais ça va. Vous ne devriez pas obtenir d'erreur ou d'exception.

Maintenant, ajoutons quelque chose à ce dieu Objectet voyons si nous l'obtenons également dans d'autres endroits?

Object.prototype.test = "Okay!";

Et voilà:

Object.prototype.test
Function.prototype.test
myFunc.prototype.test

Dans tous les cas, vous devriez voir "Okay!".

En ce qui concerne les avantages et les inconvénients de chaque méthode, vous pouvez considérer le prototypage comme une manière "plus efficace" de faire les choses, car il conserve une référence sur chaque instance plutôt que de copier la propriété entière dans chaque objet. D'un autre côté, c'est un exemple de couplage serré qui est un gros non jusqu'à ce que vous puissiez vraiment justifier la raison. thisest beaucoup plus compliqué car il est pertinent pour le contexte. Vous pouvez trouver de nombreuses bonnes ressources gratuitement sur Internet.

Cela dit, les deux méthodes ne sont que des outils linguistiques et cela dépend vraiment de vous et du problème que vous essayez de résoudre pour choisir ce qui vous convient le mieux.

Si vous avez besoin d'une propriété pour qu'elle soit pertinente pour chaque instance d'une classe, utilisez-la this. Si vous avez besoin d'une propriété pour qu'elle fonctionne de la même manière sur chaque instance, utilisez-la prototype.

Mise à jour

En ce qui concerne vos exemples d'extraits, le premier est un exemple de Singleton , il est donc judicieux d'utiliser thisdans le corps de l'objet. Vous pouvez également améliorer votre exemple en le rendant modulaire comme celui-ci (et vous n'avez pas besoin de toujours l'utiliser thiségalement).

/* Assuming it will run in a web browser */
(function (window) {
    window.myApp = {
        ...
    }
})( window );

/* And in other pages ... */
(function (myApp) {
    myApp.Module = {
        ...
    }
})( myApp );

/* And if you prefer Encapsulation */
(function (myApp) {
    myApp.Module = {
         "foo": "Foo",
         "bar": function ( string ) {
             return string;
         },
         return {
             "foor": foo,
             "bar": bar
         }
    }
})( myApp );

Votre deuxième extrait n'a pas beaucoup de sens, car vous utilisez d'abord thiset ensuite vous essayez de le pirater prototype, ce qui ne fonctionne pas, car il thisest prioritaire prototype. Je ne sais pas quelles étaient vos attentes par rapport à ce morceau de code et comment il fonctionnait, mais je vous recommande fortement de le refactoriser.

Mise à jour

Pour élaborer sur la thispriorité, prototypeje peux vous montrer un exemple et vous expliquer comment l'expliquer, mais je n'ai aucune ressource externe pour le sauvegarder.

L'exemple est très simple:

var myClass = function () { this.foo = "Foo"; };
myClass.prototype.foo = "nice try!";
myClass.prototype.bar = "Bar";

var obj = new myClass;
obj.foo;     // Still contains "Foo" ...
obj.bar;     // Contains "Bar" as expected

L'explication est, comme nous le savons, thispertinente pour le contexte. Il n'existera donc que lorsque le contexte sera prêt. Quand le contexte est prêt? Lorsque la nouvelle instance est créée! Vous devriez deviner le reste maintenant! Cela signifie que même s'il existe une prototypedéfinition, il thisest plus logique de prendre la priorité, car il s'agit de la nouvelle instance créée à ce moment.

53777A
la source
Ceci est une réponse en partie épique impressionnante! Pouvez-vous le modifier et approfondir la comparaison et le contraste des deux méthodes? Vos deux premiers paragraphes me confondent sur la méthode de conception à laquelle vous faites référence. Votre exercice est excellent! Je n'ai jamais beaucoup réfléchi au pouvoir du prototypage comme ça. Pouvez-vous également donner un exemple d'utilisation semi-détaillée du cas où utiliser chaque méthode? Merci encore, c'est super.
JDillon522
@ JDillon522 Heureux d'entendre cela. Je vais essayer de le mettre à jour dans les prochaines heures!
53777A
@ JDillon522 Je viens de faire une mise à jour rapide. J'espère que ce sera plus clair cette fois.
53777A
@ JDillon522 Un peu plus sur vos extraits de
code
Bel exemple singleton. J'ai donc utilisé thisl'exemple idiot du prototype car il thisfait référence à ses propres propriétés, y compris ses méthodes. Je n'apprends pas le meilleur de la lecture du code, mais plus de la lecture du code. ( Le morceau de MDN dessus , Object Playground (incroyable) et quelques autres). Pouvez-vous indiquer quelque chose qui explique ce que vous voulez dire par " thisa priorité sur prototype"? J'aimerais approfondir la question.
JDillon522