Fermetures anonymes auto-référencées: JavaScript est-il incomplet?

18

Le fait que les fermetures de fonctions d'auto-référencement anonymes soient si répandues en JavaScript suggère-t-il que JavaScript est une spécification incomplète? Nous en voyons tellement:

(function () { /* do cool stuff */ })();

et je suppose que tout est une question de goût, mais cela ne ressemble-t-il pas à un kludge, alors que tout ce que vous voulez, c'est un espace de noms privé? JavaScript n'a-t-il pas pu implémenter des packages et des classes appropriées?

Comparez avec ActionScript 3, également basé sur ECMAScript, où vous obtenez

package com.tomauger {
  import bar;
  class Foo {
     public function Foo(){
       // etc...
     }

     public function show(){
       // show stuff
     }

     public function hide(){
       // hide stuff
     }
     // etc...
  }
}

Contrairement aux convolutions que nous effectuons en JavaScript (ceci, à partir de la documentation de création du plugin jQuery ):

(function( $ ){

  var methods = {
    init : function( options ) { // THIS },
    show : function( ) { // IS   },
    hide : function( ) { // GOOD },
    update : function( content ) { // !!! }
  };

  $.fn.tooltip = function( method ) {

    // Method calling logic
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    

  };

})( jQuery );

J'apprécie que cette question puisse facilement dégénérer en une diatribe sur les préférences et les styles de programmation, mais je suis en fait très curieux de savoir ce que vous en pensez des programmeurs chevronnés et si cela semble naturel, comme apprendre différentes idiosyncrasies d'une nouvelle langue ou kludgy , comme une solution de contournement à certains composants de base du langage de programmation qui ne sont tout simplement pas implémentés?

Tom Auger
la source
22
"JavaScript ne pourrait-il pas implémenter ... des classes appropriées?" Non, il a déjà des prototypes appropriés. Les prototypes ne sont pas inférieurs aux classes. Ils sont différents. Les gens ont essayé d'ajouter des classes à JavaScript à plusieurs reprises et ont échoué.
Rein Henrichs
5
@Rein: Et pourtant, ActionScript l'a réussi ...
Mason Wheeler
8
@Tom il n'y a pas de "classes intégrées". Il n'y a rien de tel qu'une classe dans un langage prototypique . Vous continuez à confondre les deux paradigmes.
Rein Henrichs
1
Personnellement, je trouve la fonctionnalité de langage de fonction anonyme plus flexible que les classes. Cependant, j'aime la programmation fonctionnelle dans laquelle cet idiome est courant.
dietbuddha
1
Pour les autres, ici: brianodell.net/?page_id=516 est une excellente introduction à JavaScript en tant que langage prototypique.
Tom Auger

Réponses:

9

Je suppose que tout est une question de goût, mais cela ne ressemble-t-il pas à un kludge, alors que tout ce que vous voulez, c'est un espace de noms privé? JavaScript n'a-t-il pas pu implémenter des packages et des classes appropriées?

La plupart des commentaires vont à l'encontre du mythe selon lequel «les prototypes sont des classes de pauvres», je vais donc simplement répéter que l'OO basé sur un prototype n'est en aucun cas inférieur à l'OO basé sur une classe.

L'autre point "un kludge quand tout ce que vous voulez c'est un espace de noms privé". Vous pourriez être surpris de savoir que Scheme utilise exactement le même kludge pour définir les étendues. Cela n'a pas empêché de devenir l'exemple archétypal de la portée lexicale bien faite.

Bien sûr, dans Scheme, le "kludge" est caché derrière les macros ....

Javier
la source
1
Vous ne présentez aucune preuve pour étayer votre affirmation selon laquelle Scheme est le principal exemple de portée lexicale bien faite, ou que cela n'a rien à voir avec la façon dont Scheme a utilisé les fonctions pour définir les étendues.
DeadMG
Je ne peux pas dire que Scheme est un exemple, mais un exemple du co-créateur de JS, Brendan Eich, discutant de Scheme jouant un rôle dans la conception de JS ici: readwrite.com/2011/07/22/javascript-was-no-accident
Erik Reppen
7

Tout d'abord, quelques choses:

  1. Une autre façon de voir JavaScript est le 1 million et 1 choses que vous pouvez faire avec la fonction en tant que construction. Tout est là si vous le cherchez. Ce n'est jamais loin d'une fonction.

  2. Cette chose de création de plug-in jQuery est horrible. Je ne sais pas pourquoi ils préconisent cela. Les extensions $ devraient être des éléments à usage général que $ a déjà assez bien couvert et non des méthodes de création de widget. C'est un outil de normalisation DOM-API. Son utilisation est mieux enfouie à l'intérieur de vos propres objets. Je ne vois pas l'intérêt de l'utiliser comme un référentiel de bibliothèque d'interface utilisateur complet.

Les packages sur le Web côté client sont inutiles

Ce que je n'aime pas personnellement à propos des packages sur le Web côté client, c'est que nous prétendons essentiellement que nous faisons quelque chose que nous ne faisons vraiment pas. Dans un post Web .NET et des tas de trucs horribles qui ne sont jamais supprimés de nos amis Java, je préfère penser à un morceau de HTML avec des ressources liées comme ce qu'il est vraiment. et ne pas essayer d'apaiser les développeurs d'applications OS résistantes à l'apprentissage de nouvelles choses en prétendant que c'est autre chose. Dans JS sur le Web côté client, rien n'est "importé" à moins de faire quelque chose d'horrible avec Ajax qui fonctionne dans l'ignorance de la mise en cache du navigateur, ce que oui, beaucoup ont essayé de faire. Tout ce qui compte pour le navigateur, c'est qu'il a été chargé et interprété ou non. Nous n'avons plus de code caché sur le client quelque part disponible pour une utilisation "juste au cas où" pour de bonnes raisons. # 1 étant que je viens de décrire un plug-in et des dépendances de plug-in de navigateur pour les applications Web comme un phénomène n'a généralement pas trop bien fonctionné. Nous voulons le web maintenant. Pas après la mise à jour d'Adobe ou de Sun pour la troisième fois cette semaine.

La langue a ce dont elle a besoin pour sa structure

Les objets JS sont hautement mutables. Nous pouvons avoir des arbres de branchement d'espaces de noms à tout degré que nous jugeons utile de le faire et c'est très facile à faire. Mais oui, pour tout ce qui est réutilisable, vous devez coller la racine de n'importe quelle bibliothèque à l'espace global. De toute façon, toutes les dépendances sont liées et chargées en même temps, alors à quoi bon faire autre chose? Le point d'éviter l'espace de noms global n'est pas que quelque chose soit mauvais. C'est que trop de choses sont mauvaises parce que vous courez le risque de collisions d'espace de noms ou d'écraser accidentellement les fonctionnalités du langage de base.

Ce n'est pas parce que c'est populaire que nous le faisons correctement

Maintenant, lorsque vous voyez cela partout dans une application Web côté client:

(function(){
//lots of functions defined and fired and statement code here
})()

Le problème n'est pas que nous manquons d'outils pour structurer une application, le problème est que les gens ne valorisent pas la structure. Pour les sites jetables temporaires de 2-3 pages dans une agence de design, je n'ai pas vraiment de problème avec ça. Là où ça devient moche, c'est quand il faut construire quelque chose de maintenable, lisible et facile à modifier.

Mais lorsque vous arrivez à l'endroit où il est temps de simplement implémenter tous les objets et usines réutilisables et peut-être qu'un ou deux nouveaux vars temporaires pourraient se glisser dans ce processus, c'est une commodité.

Mais il existe des implémentations de JS avec des packages / modules

Gardez à l'esprit que dans Node.js, où de telles choses ont beaucoup plus de sens, ils ont des modules. JS, en supposant que nous pouvons éviter uber-config-hell qui empoisonne d'autres langages, est la seule chose dans l'équation et chaque fichier exécuté est sa propre portée isolée. Mais sur une page Web, la liaison d'un fichier js est elle-même l'instruction d'importation. Faire plus d'importations à la volée n'est qu'une perte de temps et de ressources, car obtenir les ressources nécessite beaucoup plus d'efforts que d'ajouter simplement des liens aux fichiers car vous en avez besoin en sachant qu'ils seront mis en cache dans un navigateur si une autre page en a besoin à nouveau. Il en va de même pour diviser l'espace global en faisant autre chose que de créer des fabriques d'objets adaptateurs comme jQuery ou des objets plus traditionnels qui couvrent un large sous-ensemble de tâches dans un domaine donné tout en occupant une place dans global. Là'http://wiki.ecmascript.org/doku.php?id=harmony:modules

Donc non, il n'y a rien de mal avec les invocateurs automatiques utilisés pour éviter la pollution globale des espaces de noms quand il y a une bonne raison d'utiliser de telles choses (le plus souvent il n'y en a pas). Et nous avons des propriétés équivalentes privées persistantes dans nos objets (définissez simplement une var dans le constructeur et ne l'exposez pas en tant que propriété).

Le fait que nous POUVONS faire de telles choses, cependant, est génial. Une utilisation intensive est un signe que les développeurs JS mûrissent peut-être encore, mais ce n'est pas un trou béant dans la langue pour quiconque n'essaie pas de forcer un paradigme dans le Web côté client qui n'a tout simplement pas de sens ici.

Erik Reppen
la source
Aux votants, pourriez-vous expliquer pourquoi? Quand quelqu'un écrit autant, je pense qu'il mérite une explication!
Songo
+1 bonne réponse, je ne sais pas pourquoi le vote négatif avant.
suppliez le
Rédaction impressionnante et grande perspective. J'aime aussi le "juste parce que c'est un trope ne veut pas dire qu'il a raison". Je pense que mon problème est que je suis plus à l'aise avec des langages plus stricts, ce qui (IMO) contribue à l'efficacité du développement de plusieurs façons. JavaScript semble vraiment insipide avec peu de freins et contrepoids intégrés: vous pouvez faire ce que vous voulez, d'où le paysage là-bas est un gâchis d'idiomes et de pratiques. Il est difficile de définir la "bonne" façon d'aborder votre structure de codage. Bien que je convienne que pour les petits boulots rapides, ce n'est pas une grande préoccupation.
Tom Auger
1
OMI, il y a beaucoup de choses que vous pouvez faire avec beaucoup de corde à part vous accrocher et vous apprenez à écrire du code robuste plus rapidement à partir d'auto-accrochages occasionnels qui se produisent moins fréquemment lorsque vous développez de meilleures habitudes, mais je ne prétendrai pas que c'est pour tout le monde ou un candidat idéal pour chaque emploi. Je soupçonne que plus vous en apprenez à ce sujet, plus vous le trouverez tolérable. J'ai l'impression de manquer la moitié de mon cerveau lorsque j'essaie de faire des choses dans des langages sans fonctions ou objets de première classe aussi flexibles / mutables que ceux de JS.
Erik Reppen
4

L'autre chose qui vous manque, c'est que javscript doit être rétrocompatible. Si vous essayez d'introduire la syntaxe d'un package, cela pourrait vraiment casser le Web de manière folle. Ce serait mauvais! Doug Crockford en a parlé à différents moments et pourquoi les tentatives pour l'ajouter ont échoué.

Zachary K
la source
C'est un bon point. Et pourtant ActionScript l'a géré, en mettant simplement une nouvelle version. Lors de la définition de votre balise de script, vous avez toujours pu spécifier la version JavaScript, donc "casser" les sites existants ne devrait pas poser de problème.
Tom Auger
1
En pratique, la plupart des balises de script sur le net n'ont pas de numéro de version. Pour être honnête, je ne suis pas sûr de tous les problèmes sur celui-ci, mais je sais que les gens qui pensent à ce genre de choses ont décidé que ce n'était pas faisable.
Zachary K
1
@Tom: Adobe a également un contrôle total sur la plate-forme Flash. Aucune entité n'a le contrôle total sur toutes les plates-formes JS. De plus, le simple fait de coller un numéro de version pour un script JS dans un navigateur signifierait que vous ne supportez pas les anciens navigateurs ou que vous devez écrire deux scripts. Ainsi, il est un problème.
Jeremy Heiler
2

Oui, c'est un coup de coude.

Beaucoup de gens disent que "les prototypes ne sont pas inférieurs aux classes". Je ne suis pas d'accord, mais c'est une question de préférence. Mais ce n'est même pas le vrai problème avec JavaScript - le problème est qu'il a été initialement conçu comme un langage de script rapide et sale pour créer des choses comme des boutons animés. Au milieu des années 90, personne n'a jamais pensé que JavaScript serait invité à faire certaines des choses folles qu'il fait maintenant.

Mike Baranczak
la source
6
Je ne suis pas d'accord, JavaScript est en fait vraiment génial. Le fait qu'il ait été regroupé avec un DOM sous-spécifié et mutuellement incompatible est à l'origine de tous les problèmes.
Dean Harding
2
Qu'est-ce que cela a à voir avec les prototypes?
Erik Reppen
2

Les fonctions auto-appelantes anonymes sont plus proches des modules que des classes. Il est ennuyeux que la valeur par défaut pour javascript soit de s'exécuter dans la portée globale. Le comité travaillant sur JS.next envisage sérieusement d'ajouter des modules, afin que vous ne déposiez pas vos variables locales dans la portée globale. Heureusement, les fonctions de Javascript ont une sémantique si pratique que nous pouvons utiliser une fonction anonyme comme portée privée avec une relative facilité.

Je ne vois pas du tout comment les classes entrent vraiment dans la discussion, sauf qu'elles sont la construction de portée de haut niveau dans de nombreuses langues. Une meilleure construction module / package / please-give-me-a-local-scope-so-i-don't-my-variables-in-the-global-environment serait très agréable à avoir.

Sean McMillan
la source
1

Vous voudrez peut-être jeter un œil à ExtJS 3 et 4 où ils ont réussi à implémenter assez bien les espaces de noms.

- ajouté après -1

Mon point ici était, il est possible de cacher toutes ces «circonvolutions» et d'avoir toujours un code assez convivial comme ceci:

Ext.ns('com.tomauger');
Ext.Loader.load('bar.js'); //unfortunately filname needs to be used
MyNameSpace.Foo = {
   //...
}
Mchl
la source