Qu'est-ce qui ne va pas avec ce style de codage JavaScript? (Fermeture contre prototypes)

Nous avons discuté de la meilleure façon de gérer les objets dans notre application JS, en étudiant le livre de Stoyan Stefanov, en lisant des messages SO sans fin sur «nouveau», «ce», «prototype», les fermetures, etc. (le fait qu'il y en a tellement Ont tant de théories concurrentes, suggère qu'il n'y a pas de réponse complètement évidente).

Supposons donc que nous ne nous soucions pas des données privées . Nous nous contentons de faire confiance aux utilisateurs et aux développeurs à ne pas déranger dans des objets en dehors des façons que nous définissons.

Compte tenu de cela , qu'est-ce qui (autre que ce qui semble défier des décennies de style OO et d'histoire) serait erroné avec cette technique?

// namespace to isolate all PERSON's logic var PERSON = {}; // return an object which should only ever contain data. // The Catch: it's 100% public PERSON.constructor = function (name) { return { name: name } } // methods that operate on a Person // the thing we're operating on gets passed in PERSON.sayHello = function (person) { alert (person.name); } var p = PERSON.constructor ("Fred"); var q = PERSON.constructor ("Me"); // normally this coded like 'p.sayHello()' PERSON.sayHello(p); PERSON.sayHello(q); 

Évidemment:

  1. Il n'y aurait rien pour empêcher quelqu'un de «p» mutuellement de manière impie, ou simplement la logique de PERSON se terminant se répandit sur tout le lieu. ( C'est vrai avec la «nouvelle» technologie canonique aussi).
  2. Ce serait un malheur mineur de passer 'p' dans toutes les fonctions que vous vouliez utiliser.
  3. C'est une approche étrange .

Mais ces raisons sont-elles assez bonnes pour le rejeter? Du côté positif:

  1. Il est efficace , car (sans doute) s'oppose aux fermetures avec déclaration de fonction répétitive.
  2. Il semble très simple et compréhensible , par opposition à jouer de «cela» partout.

Le point clé est le précédent de la vie privée . Je sais que je serai claqué pour cela, mais je cherche des commentaires. À votre santé.

Il n'y a rien de néfaste à ce sujet. Mais il renonce à de nombreux avantages inhérents à l'utilisation du système prototype de Javascript.

  • Votre objet ne sait rien de lui-même que ce soit un objet littéral. Donc instanceof ne vous aidera pas à identifier son origine. Vous serez bloqué en utilisant uniquement le typage de canard.
  • Vos méthodes sont essentiellement des fonctions statiques dénommées par les noms , où vous devez vous répéter en passant l'objet en tant que premier argument. En ayant un objet prototypé, vous pouvez profiter de l'envoi dynamique , de sorte que p.sayHello() puisse faire différentes choses pour PERSON ou ANIMAL fonction du type de Javascript connu. C'est une forme de polymorphisme . Votre approche vous oblige à nommer (et peut-être faire une erreur) le type chaque fois que vous appelez une méthode.
  • Vous n'avez pas besoin d'une fonction constructor , car les fonctions sont déjà des objets. Votre variable PERSON peut aussi bien être la fonction constructeur.

Ce que vous avez fait ici, c'est créer un modèle de module (comme un espace de noms).

Voici un autre modèle qui conserve ce que vous avez, mais fournit les avantages ci-dessus:

 function Person(name) { var p = Object.create(Person.prototype); p.name = name; // or other means of initialization, use of overloaded arguments, etc. return p; } Person.prototype.sayHello = function () { alert (this.name); } var p = Person("Fred"); // you can omit "new" var q = Person("Me"); p.sayHello(); q.sayHello(); console.log(p instanceof Person); // true var people = ["Bob", "Will", "Mary", "Alandra"].map(Person); // people contains array of Person objects 

Ouais, je ne comprends pas vraiment pourquoi vous essayez d'esquiver l'approche du constructeur ou pourquoi ils ont même ressenti le besoin de coller le sucre syntaxique sur les constructeurs de fonctions (Object.create et bientôt les classes) lorsque les constructeurs sont eux-mêmes élégants, flexibles, Et une approche parfaitement raisonnable pour OOP, peu importe combien de raisons lameuses sont données par des gens comme Crockford pour ne pas les aimer (parce que les gens oublient d'utiliser le nouveau mot-clé – sérieusement?). JS est fortement axé sur les fonctions et sa mécanique OOP n'est pas différente. Il est préférable d'embrasser cela que de cacher, IMO.

Tout d'abord, vos points énumérés sous "Évidemment"

  1. Il ne faut pas moins mentionner dans JavaScript. Des degrés élevés de mutabilité sont le sous-design. Nous n'avons pas peur de nous-mêmes ou d'autres développeurs en JavaScript. Le paradigme public ou public n'est pas utile car il nous protège de la bêtise, mais plutôt parce qu'il facilite la compréhension de l'intention derrière le code d'un autre développeur.

  2. L'effort en invoquant n'est pas le problème. Les tracas arrivent plus tard quand on ignore pourquoi vous avez fait ce que vous avez fait là-bas. Je ne vois pas vraiment ce que vous essayez d'atteindre pour que les approches de base ne soient pas meilleures pour vous.

  3. C'est JavaScript. Il a été étrange pour tous, sauf pour les développeurs de JS depuis des années. Ne suez pas cela si vous trouvez une meilleure façon de faire quelque chose qui fonctionne mieux pour résoudre un problème dans un domaine donné qu'une solution plus typique pourrait. Assurez-vous simplement de comprendre le point de l'approche plus typique avant d'essayer de la remplacer car tant d'autres sont en provenance d'autres paradigmes linguistiques. Il est facile de faire des choses triviauses avec JS, mais une fois que vous êtes au point où vous souhaitez obtenir plus d'OOP-driven, apprenez tout ce que vous pouvez sur la façon dont fonctionne le langage principal afin que vous puissiez appliquer un peu plus de scepticisme aux opinions populaires là-bas Par des personnes qui font un jeu de hasard pour devenir plus effrayantes et plus criblées de pièges mortels que ce n'est vraiment.

Maintenant, vos points sous "côté positif"

  1. Tout d'abord, la définition de la fonction répétitive n'était vraiment que quelque chose à craindre dans le scénario de la boucle lourde. Si vous produisiez régulièrement des objets en assez grande quantité assez rapidement pour que les définitions de méthodes publiques non prototypes soient un problème de performance, vous rencontrerez probablement des problèmes d'utilisation de la mémoire avec des objets non triviaux peu importe peu importe. Je parle dans le temps passé, cependant, parce que ce n'est plus vraiment un problème pertinent de toute façon. Dans les navigateurs modernes, les fonctions définies à l'intérieur d'autres fonctions sont généralement des performances améliorées en raison de la façon dont les compilateurs JIT modernes fonctionnent. Quel que soit le navigateur que vous supportez, quelques funcs définis par objet sont sans objet à moins d'attendre des dizaines de milliers d'objets.

  2. Sur la question simple et compréhensible, ce n'est pas pour moi parce que je ne vois pas ce que vous avez gagné ici. Maintenant, au lieu d'avoir un objet à utiliser, je dois utiliser à la fois l'objet et le pseudo-constructeur ensemble, ce qui, si je ne regardais pas la définition, m'impliquerait la fonction que vous utilisez avec un mot-clé «nouveau» pour créer des objets . Si j'étais nouveau dans votre base de code, je gaspillais beaucoup de temps à essayer de comprendre pourquoi vous l'avez fait de cette façon pour éviter de vous soulever d'autres préoccupations, je n'ai pas compris.

Mes questions seraient:

Pourquoi ne pas simplement ajouter toutes les méthodes dans le litre d'objet dans le constructeur en premier lieu? Il n'y a pas de problème de performance là-bas et il n'y a jamais eu vraiment la seule autre victoire possible, c'est que vous souhaitez ajouter de nouvelles méthodes après avoir créé de nouveaux objets, mais c'est ce dont nous utilisons un prototype pour les constructeurs appropriés (Les prototypes de méthodes btw sont parfaits pour la mémoire dans les anciens navigateurs car ils ne sont définis qu'une seule fois).

Et si vous devez continuer à passer l'objet dans les méthodes pour savoir quelles sont les propriétés, pourquoi souhaitez-vous même des objets? Pourquoi pas seulement des fonctions qui s'attendent à des objets simples de type structure de données avec certaines propriétés? Ce n'est plus vraiment OOP.

Mais mon point de critique principal

Vous manquez le point principal d'OOP, ce qui signifie que JavaScript ne permet pas de ne pas se cacher des personnes que la plupart des langues. Considérer ce qui suit:

 function Person(name){ //var name = name; //<--this might be more clear but it would be redundant this.identifySelf = function(){ alert(name); } } var bob = new Person(); bob.identifySelf(); 

Maintenant, modifiez le nom du bob identifié avec, sans écraser l'objet ou la méthode, qui sont les deux choses que vous ne feriez que s'il était clair que vous ne vouliez pas travailler avec l'objet tel que conçu et construit à l'origine. Vous ne pouvez bien sûr pas. Cela rend clair pour quiconque voit cette définition que le nom est effectivement une constante dans ce cas. Dans un constructeur plus complexe, il établirait que la seule chose autorisée à modifier ou à modifier le nom est l'instance elle-même, à moins que l'utilisateur n'ajoute une méthode de validation non valide qui serait bête parce que ce serait essentiellement (en regardant Java Enterprise Beans) ASSURER LE OBJECTIF CENTRAL D'OOP.

La division claire de la responsabilité est la clé

Oubliez les mots clés qu'ils mettent dans chaque livre pour une seconde et réfléchissez à ce qui est l'ensemble. Avant OOP, tout était simplement une pile de fonctions et de structures de données sur lesquelles toutes ces fonctions fonctionnaient. Avec OOP, vous disposez principalement d'un ensemble de méthodes regroupées avec un ensemble de données que seul l'objet en soi change.

Donc, disons que quelque chose a mal tourné vers la sortie:

  • Dans notre pile de fonctions strictement procédurales, il n'y a pas de limite réelle au nombre de mains qui pourraient avoir détruit ces données. Nous pourrions avoir une bonne gestion des erreurs, mais une fonction pourrait se ramifier de telle sorte que le coupable original est difficile à repérer.

  • Dans un design OOP approprié où les données sont généralement derrière un portier d'objet, je sais qu'un seul objet peut effectivement rendre les changements responsables.

Les objets qui exposent toutes leurs données la plupart du temps ne sont que légèrement mieux que l'ancienne approche procédurale. Tout ce qui fait vraiment est vous donner un nom pour catégoriser les méthodes lâchement liées.

Much Ado About 'this'

Je n'ai jamais compris l'attention excessive accordée au mot-clé 'this' qui est désordonné et confus. Ce n'est vraiment pas un gros problème. 'This' identifie l'instance avec laquelle vous travaillez. C'est tout. Si la méthode n'est pas appelée comme une propriété, elle ne connaîtra pas l'instance à rechercher, donc elle est par défaut pour l'objet global. C'était idiot (indéfini aurait été mieux), mais il ne fonctionne pas correctement dans ce scénario devrait être attendu dans une langue où les fonctions sont également portables comme des données et peuvent être attachés à d'autres objets très facilement. Utilisez 'this' dans une fonction lorsque:

  • Il est défini et appelé comme propriété d'une instance.

  • Il est passé comme un gestionnaire d'événements (qui l'appellera comme membre de la chose écoutée).

  • Vous utilisez un appel ou appliquez des méthodes pour l'appeler comme propriété d'un autre objet temporairement sans l'affecter en tant que tel.

Mais rappelez-vous, c'est l'appel qui compte vraiment. L'attribution d'une méthode publique à certains var et l'appel à partir de cette var fera la chose globale ou lancera une erreur en mode strict. Sans être référencé comme propriétés de l'objet, les fonctions ne concernent vraiment que la portée dans laquelle elles ont été définies (leurs fermetures) et ce que vous passez.