JavaScript: The Good Parts – Comment ne pas utiliser `new` du tout

Le livre de Crockford, JavaScript: The Good Parts , indique (à la page 114) que les fonctions du constructeur doivent toujours être nommées avec une lettre majuscule initiale (c.-à-d. Point) et que les noms de fonctions avec des majuscules initiales ne doivent être utilisés qu'avec les fonctions du constructeur ( Tout le reste devrait être lowerCase).

Cette convention nous aide à éviter d'oublier d'utiliser le new opérateur avec des fonctions constructeur.

Il poursuit en disant que "[une] meilleure stratégie d'adaptation est de ne pas utiliser de new du tout".

Ma question est la suivante: comment programmez-nous JavaScript sans utiliser de new ?

  • Nous pouvons éviter les new Object() et new Array() avec literal {} and [] .
  • Nous pouvons éviter le new Number() , le new Boolean() et le new String() avec 0 , true et '' .
  • Nous pouvons éviter de new RegExp() avec quelque chose comme /pattern/ .

Comment éviter la new Date() ?

Et, surtout, comment évitons-nous d'utiliser de new objets personnalisés?

Crockford donne un exemple pour une fonction de création d'objet comme cela aurait été fourni par JS lui-même dans l'une de ses conversations Javascript disponible sur http://developer.yahoo.com/yui/theater/

Cependant, l'équipe YUI (3) utilise elle-même "nouveau", et ils suivent ses recommandations (puisqu'il est l'architecte JS de Yahoo JS (UPDATE: il a progressé, mais la déclaration était vraie lorsque cette réponse a été écrite à l'origine). Cette déclaration particulière était plus sur un niveau «académique», ce qui aurait été DIT QUE la langue a été conçue «juste» et non avec des restes de l'héritage de classe. Il (IMHO à juste titre) dit que la façon dont il s'est avéré JS est conflictuel, basé sur un prototype mais avec cette seule chose des langages d'héritage "classe classique".

Cependant, JS est tel qu'il est alors allez et utilisez "nouveau".

Vous pouvez trouver sa fonction de création d'objet ici: http://javascript.crockford.com/prototypal.html

  if (typeof Object.create !== 'function') { Object.create = function (o) { function F() {} F.prototype = o; return new F(); }; } newObject = Object.create(oldObject); 

EDIT: Mise à jour pour utiliser la dernière version de cette fonction de Crockford – il y en a trois.

MISE À JOUR Juin 2015: Nous avons eu Object.create(...) depuis un bon moment maintenant, que tous les navigateurs actuels prennent en charge (y compris IE 9 et plus), donc il n'était pas nécessaire d'utiliser la fonction de Crockford.

Cependant , il s'avère que si vous utilisez Object.create vous Object.create vous assurer que vous ne le faites pas beaucoup: cette fonction est plus lente que l'utilisation d'un new Constructor() !

Voir http://mrale.ph/blog/2014/07/30/constructor-vs-objectcreate.html pour une explication (pour le moteur V8), et voir http://jsperf.com/object-create-vs- Crockford-vs-jorge-vs-constructor / 62 pour une démo de performance.

Une autre raison de ne pas tourner le dos sur le new Constructor(...) est que les classes ES6 verront certainement une adoption étendue, même si la seule raison pour laquelle la plupart des développeurs Javascript proviennent de langues basées sur les classes.

Consultez également cet article, qui plaide pour Object.create : http://davidwalsh.name/javascript-objects-deconstruction

Vous l'aimez ou non, en particulier dans les projets que vous souhaitez partager avec un large éventail de personnes (dans l'espace et le temps – ce qui signifie directement ou pas au fil du temps, d'autres personnes qui vous remplacent), il y a plus de raisons d'utiliser de new .

MISE À JOUR Septembre 2015: Pour moi, j'ai commencé à utiliser ES 2015 Javascript pour tout – en utilisant io.js et / ou Babel. Je n'utilise pas non plus de new dans mes projets, à l'exception de la version JavaScript intégrée, comme une new Error(...) . Je préfère utiliser l'approche fonctionnelle beaucoup plus puissante, j'ignore complètement le système d'objets. [my-object].prototype et this est complètement disparu de mes projets. Pour la plus longue période, j'ai été très sceptique de ces idées "parce que les objets fonctionnent très bien". Mais après avoir essayé très à contrecoeur au début d'un nouveau projet (io.js), il a "cliqué" et je ne comprends pas pourquoi j'ai perdu deux décennies. D'accord, pas tout à fait, aujourd'hui, les moteurs et le matériel JS sont beaucoup plus propices à ce style. Surtout avec ES 2015, je recommande de donner un style fonctionnel entièrement exempt de tout this et class (le nouveau mot-clé ES 2015 ou le concept entier, basé sur l'utilisation de constructorFn.prototype ), un essai. Cela peut vous prendre quelques semaines, mais une fois qu'il "clique", je vous promets de ne jamais revenir en arrière – pas volontairement. C'est beaucoup plus pratique et puissant.

Je ne sais pas comment éviter la new Date() ou le new XMLHttpRequest() non plus. Mais je sais comment éviter d'utiliser de nouveaux pour mes propres types.

Tout d'abord, je commence par Object.create() . C'est une méthode ES5, donc il n'est pas disponible partout. Je l'ajoute en utilisant es5-shim , alors je suis prêt à partir.

J'aime le modèle de module, alors je commence par envelopper mon type dans une fonction anonyme auto-exécutante, var Xyz = (function() {...})() . Cela signifie que j'ai un espace privé pour travailler sans faire de gâchis dans l'espace de noms global. Je renvoie un objet avec une fonction create() et un prototype propriété. La fonction create() est pour les utilisateurs de mon type. Quand ils en veulent un, ils appellent Xyz.create() , et récupèrent un nouvel objet initialisé de mon type. La propriété prototype est disponible si les gens souhaitent hériter.

Voici un exemple:

 var Vehicle = (function(){ var exports = {}; exports.prototype = {}; exports.prototype.init = function() { this.mph = 5; }; exports.prototype.go = function() { console.log("Going " + this.mph.toString() + " mph."); }; exports.create = function() { var ret = Object.create(exports.prototype); ret.init(); return ret; }; return exports; })(); 

Et l'héritage ressemble à ceci:

 var Car = (function () { var exports = {}; exports.prototype = Object.create(Vehicle.prototype); exports.prototype.init = function() { Vehicle.prototype.init.apply(this, arguments); this.wheels = 4; }; exports.create = function() { var ret = Object.create(exports.prototype); ret.init(); return ret; }; return exports; })(); 

Ne pas utiliser de new et à l'aveuglette Crockford est stupide.

Comprenez JavaScript et écrivez un bon code. L'utilisation du new mot-clé est la pierre angulaire de JavaScript OO.

Vous allez manquer un bon code JavaScript en évitant les new .

Plutôt que de découper arbitrairement d'énormes morceaux hors de votre boîte à outils, apprenez-le et utilisez-le correctement à la place.

Crockford a l'habitude de dire que tout en JavaScript qui lui a déjà donné un bug dans son code est mauvais.

Je voudrais personnellement continuer à dire que «[une] meilleure stratégie d'adaptation est d'être compétente».

Vous pouvez éviter de new en créant des fonctions d'usine:

 var today = Date.getToday(); 

(Dans le cas où vous vous demandiez, vous ne pouvez pas l' éviter sur la fonction d'usine elle-même 🙂

 Date.getToday = function() { return new Date(); }; 

Bien que je ne pense que vous devriez créer de telles fonctions si elle ajoute une valeur sémantique (comme dans le cas ci-dessus) ou si vous pouvez par défaut certains paramètres du constructeur. En d'autres termes, ne le faites pas simplement pour éviter d' utiliser de new .

Cette question a déjà été posée et répondu: le mot-clé "nouveau" de JavaScript est-il considéré comme nuisible?

Comme Raynos a dit, suivre aveuglément Crockford (ou quelqu'un d'autre pour cette question) sans comprendre pourquoi ils disent ce qu'ils font, est ridicule.

Je pense que son conseil de ne pas utiliser le tout est conceptuel (universitaire) et ne doit pas être pris littéralement. La classe Date est une exception parfaite à la règle, car de quelle autre façon pouvez-vous obtenir un objet de date actuel (ou arbitraire) à l'aide de ECMAScript standard?

Toutefois, en ce qui concerne l'utilisation de new objets personnalisés, vous pouvez utiliser quelques stratégies. L'une consiste à utiliser des méthodes semblables à celles d'usine plutôt que des constructeurs qui pourraient prendre comme argument une instance d'objet pour «bénir» dans votre nouveau type ou utiliser un nouveau texte d'objet par défaut. Considérer ce qui suit:

 var newCar = function(o) { o = o || {}; // Add methods and properties to "o"... return o; } 
 function F() { return { /* your fields and methods here */ } } 

Vous pouvez éviter "nouveau" en renvoyant un objet anonyme et en utilisant une fermeture dans votre constructeur. Cela vous aide également à cacher des données privées.

Considérer:

 function SomeCounter(start) { var counter = start; return { getCounter : function() { return counter; }, increaseCounter : function() { counter++; } }; } 

Maintenant, pour utiliser cela, tout ce que vous devez faire est

 var myCounter = SomeCounter(5); myCounter.increaseCounter(); console.log(myCounter.getCounter()); //should log 6 

La beauté de cela est que vous n'avez pas besoin de vous souvenir d'utiliser "nouveau", mais si vous le faites, cela ne vous blessera pas.

 var myCounter = new SomeCounter(5); //still works 

Vous êtes coincé avec l'utilisation de «nouveau» pour instancier les objets d'autres personnes. Mais pour votre propre code, vous pouvez éviter les pièges de 'this' et 'new'.

(Par ailleurs, le problème n'est vraiment pas avec «nouveau» lui-même. Mais un objet implanté avec «nouveau» utilise probablement «ceci» en interne. L'utilisation de «ceci» conduit à des bugs fréquents et subtils, car javascript 'Exige que l' appelant effectue un travail supplémentaire pour lier la méthode appelée à l'objet droit, et les liaisons incorrectes sont difficiles à peler ou à détecter autrement avant l'exécution.)

Version courte:

Pour éviter d'utiliser «nouveau» pour instancier des objets que vous écrivez, il suffit de retourner un objet à partir de n'importe quelle fonction. À l'intérieur de cette fonction, attachez des méthodes à cet objet et effectuez une initialisation. Vous avez terminé – cette fonction est à la fois votre définition de classe et votre constructeur.

Version longue, par exemple:

Le modèle de «soi» suivant évite l'utilisation à la fois de «nouveau» et de «ceci». Bien que le modèle stocke les copies de la méthode dans chaque objet, cela ne sera important que si vous créez beaucoup d'objets au moment de l'exécution. Si tel est le cas, vous pouvez toujours utiliser le modèle «flyweight» à partir de http://justjs.com/posts/this-considered-harmful . (Alors que les exemples sur cette page utilisent toujours «ceci» un peu, c'est un léger réglage pour adapter ceux-ci au schéma suivant aussi.)

 // this function defines a "class" -- the whole function is the constructor function Foo(x) { // create/init whatever object type you want here var self = {x: x}; // if subclassing, for instance, you can do: // var self = Baz(x); // public method self.foo = function() { return self.x; }; // public method self.bar = function() { console.log(self.x); logger(); }; // private method function logger() { console.log(self.x); } // ...more constructor bits can go here // don't forget to return self return self; } var f = Foo(1); var g = Foo(2); setTimeout(f.bar, 1000); setTimeout(g.bar, 1000); console.log(g.foo()); // 2 gx = 5; console.log(f.foo()); // 1 console.log(g.foo()); // 5 // ...then, 1 second later: // 1 (from f.bar) // 1 (from f.logger) // 5 (from g.bar) // 5 (from g.logger) // blows up if uncommented // f.logger();