Écrire du JavaScript modulaire avec AMD, CommonJS et ES Harmony

Tweet

Modularité L’importance de découpler votre application

Quand nous disons qu’une application est modulaire, nous voulons généralement dire qu’elle est composée d’un ensemble de morceaux de fonctionnalité distincts et hautement découplés stockés dans des modules. Comme vous le savez probablement, le couplage lâche facilite la maintenabilité des apps en supprimant les dépendances lorsque cela est possible. Lorsque cela est mis en œuvre efficacement, il est assez facile de voir comment les changements apportés à une partie d’un système peuvent en affecter une autre.

Contrairement à certains langages de programmation plus traditionnels cependant, l’itération actuelle de JavaScript (ECMA-262) ne fournit pas aux développeurs les moyens d’importer de tels modules de code d’une manière propre et organisée. C’est l’un des soucis avec les spécifications qui n’ont pas nécessité une grande réflexion jusqu’à plus récemment où le besoin d’applications JavaScript plus organisées est devenu évident.

Au lieu de cela, les développeurs à l’heure actuelle sont laissés à retomber sur des variations du module ou des modèles littéraux d’objets. Avec beaucoup d’entre eux, les scripts de module sont enfilés dans le DOM avec des espaces de noms étant décrits par un seul objet global où il est toujours possible d’encourir des collisions de nommage dans votre architecture. Il n’y a également aucun moyen propre de gérer la gestion des dépendances sans un certain effort manuel ou des outils tiers.

Alors que des solutions natives à ces problèmes arriveront dans ES Harmony, la bonne nouvelle est que l’écriture de JavaScript modulaire n’a jamais été aussi facile et que vous pouvez commencer à le faire dès aujourd’hui.

Dans cet article, nous allons examiner trois formats pour écrire du JavaScript modulaire : AMD, CommonJS et les propositions pour la prochaine version de JavaScript, Harmony.

Préface Une note sur les chargeurs de script

Il est difficile de discuter des modules AMD et CommonJS sans parler de l’éléphant dans la pièce – les chargeurs de script. À l’heure actuelle, le chargement de script est un moyen d’atteindre un objectif, cet objectif étant un JavaScript modulaire qui peut être utilisé dans les applications aujourd’hui – pour cela, l’utilisation d’un chargeur de script compatible est malheureusement nécessaire. Afin de tirer le meilleur parti de cet article, je recommande d’acquérir une compréhension de base de la façon dont les outils de chargement de script populaires fonctionnent afin que les explications des formats de module aient un sens dans le contexte.

Il existe un certain nombre d’excellents chargeurs pour gérer le chargement de modules dans les formats AMD et CJS, mais mes préférences personnelles sont RequireJS et curl.js. Les tutoriels complets sur ces outils sortent du cadre de cet article, mais je peux recommander la lecture du post de John Hann sur curl.js et la documentation de l’API RequireJS de James Burke pour en savoir plus.

D’un point de vue de production, l’utilisation d’outils d’optimisation (comme l’optimiseur RequireJS) pour concaténer les scripts est recommandée pour le déploiement lorsque vous travaillez avec de tels modules. Fait intéressant, avec la cale AMD Almond, RequireJS n’a pas besoin d’être roulé dans le site déployé et ce que vous pourriez considérer comme un chargeur de script peut être facilement déplacé en dehors du développement.

Cela dit, James Burke dirait probablement que le fait de pouvoir charger dynamiquement des scripts après le chargement de la page a toujours ses cas d’utilisation et RequireJS peut aider à cela aussi. Avec ces notes en tête, commençons.

AMD Un format pour écrire du JavaScript modulaire dans le navigateur

L’objectif global pour le format AMD (Asynchronous Module Definition) est de fournir une solution pour le JavaScript modulaire que les développeurs peuvent utiliser aujourd’hui. Il est né de l’expérience du monde réel de Dojo utilisant XHR+eval et les partisans de ce format voulaient éviter que toute solution future ne souffre des faiblesses de celles du passé.

Le format de module AMD lui-même est une proposition pour définir des modules où le module et les dépendances peuvent être chargés de manière asynchrone. Il présente un certain nombre d’avantages distincts, notamment le fait d’être à la fois asynchrone et très flexible par nature, ce qui supprime le couplage étroit que l’on peut trouver habituellement entre le code et l’identité du module. De nombreux développeurs aiment l’utiliser et on pourrait le considérer comme un tremplin fiable vers le système de modules proposé pour ES Harmony.

AMD a commencé comme un projet de spécification pour un format de module sur la liste CommonJS mais comme il n’a pas été en mesure d’atteindre un consensus complet, le développement ultérieur du format s’est déplacé vers le groupe amdjs.

Aujourd’hui, il est adopté par des projets tels que Dojo (1.7), MooTools (2.0), Firebug (1.8) et même jQuery (1.7). Bien que le terme CommonJS AMD format ait été vu dans la nature à l’occasion, il est préférable de s’y référer comme juste AMD ou Async Module support car tous les participants de la liste CJS n’ont pas souhaité le poursuivre.

Note : Il fut un temps où la proposition était appelée Modules Transport/C, cependant comme la spécification n’était pas destinée à transporter des modules CJS existants, mais plutôt, à définir des modules, il était plus logique d’opter pour la convention de dénomination AMD.

Démarrer avec les modules

Les deux concepts clés dont vous devez être conscient ici sont l’idée d’une méthode define pour faciliter la définition des modules et une méthode require pour gérer le chargement des dépendances. define est utilisé pour définir des modules nommés ou non nommés basés sur la proposition en utilisant la signature suivante :

Comme vous pouvez le dire par les commentaires en ligne, le module_id est un argument optionnel qui n’est typiquement requis que lorsque des outils de concaténation non-AMD sont utilisés (il peut y avoir quelques autres cas limites où il est utile aussi). Lorsque cet argument est omis, nous appelons le module anonyme.

Lorsque l’on travaille avec des modules anonymes, l’idée de l’identité d’un module est DRY, ce qui rend trivial d’éviter la duplication des noms de fichiers et du code. Comme le code est plus portable, il peut être facilement déplacé vers d’autres emplacements (ou autour du système de fichiers) sans avoir besoin de modifier le code lui-même ou de changer son identité. Le module_id est équivalent aux chemins de dossier dans les paquets simples et lorsqu’il n’est pas utilisé dans les paquets. Les développeurs peuvent également exécuter le même code sur plusieurs environnements simplement en utilisant un optimiseur AMD qui fonctionne avec un environnement CommonJS tel que r.js.

Retour à la signature define, l’argument dependencies représente un tableau de dépendances qui sont requises par le module que vous définissez et le troisième argument (‘definition function’) est une fonction qui est exécutée pour instancier votre module. Un module barebone pourrait être défini comme suit :

Comprendre AMD : define()

require d’autre part est typiquement utilisé pour charger du code dans un fichier JavaScript de haut niveau ou dans un module si vous souhaitez récupérer dynamiquement des dépendances. Voici un exemple de son utilisation :

Comprendre AMD : require()

Dépendances chargées dynamiquement

Comprendre AMD : plugins

Voici un exemple de définition d’un plugin compatible AMD :

Remarque : bien que css ! soit inclus pour le chargement des dépendances CSS dans l’exemple ci-dessus, il est important de se rappeler que cette approche a quelques réserves, comme le fait qu’il n’est pas entièrement possible d’établir quand le CSS est entièrement chargé. Selon la façon dont vous abordez votre construction, il peut également en résulter que CSS soit inclus comme une dépendance dans le fichier optimisé, donc utilisez CSS comme une dépendance chargée dans de tels cas avec prudence.

Loading AMD Modules Using require.js

Loading AMD Modules Using curl.js

Modules With Deferred Dependencies

Why Is AMD A Better Choice For Writing Modular JavaScript?

  • Fournit une proposition claire sur la façon d’aborder la définition de modules flexibles.
  • Significativement plus propre que les solutions actuelles d’espace de noms global et de balise <script> sur lesquelles beaucoup d’entre nous comptent. Il y a un moyen propre de déclarer les modules autonomes et les dépendances qu’ils peuvent avoir.
  • Les définitions de modules sont encapsulées, ce qui nous aide à éviter la pollution de l’espace de noms global.
  • Fonctionne mieux que certaines solutions alternatives (par exemple CommonJS, que nous examinerons prochainement). N’a pas de problèmes avec le cross-domain, le local ou le débogage et n’a pas une dépendance sur les outils côté serveur à utiliser. La plupart des chargeurs AMD prennent en charge le chargement des modules dans le navigateur sans processus de construction.
  • Prend une approche de « transport » pour inclure plusieurs modules dans un seul fichier. D’autres approches comme CommonJS doivent encore se mettre d’accord sur un format de transport.
  • Il est possible de charger paresseusement des scripts si cela est nécessaire.

Modules AMD avec Dojo

Définir des modules compatibles AMD en utilisant Dojo est assez simple. Comme ci-dessus, définissez toutes les dépendances du module dans un tableau comme premier argument et fournissez un callback (factory) qui exécutera le module une fois que les dépendances auront été chargées. e.g:

define(, function( Tooltip ){ //Our dijit tooltip is now available for local use new Tooltip(...);});

Notez la nature anonyme du module qui peut maintenant être à la fois consommé par un chargeur asynchrone Dojo, RequireJS ou le chargeur de module standard dojo.require() que vous avez peut-être l’habitude d’utiliser.

Pour ceux qui s’interrogent sur le référencement de module, il y a quelques gotchas intéressants qu’il est utile de connaître ici. Bien que la manière préconisée par AMD de référencer les modules les déclare dans la liste des dépendances avec un ensemble d’arguments correspondants, ceci n’est pas supporté par le système de construction de Dojo 1.6 – cela ne fonctionne vraiment que pour les chargeurs conformes à AMD. e.g:

define(, function( cookie, Tooltip ){ var cookieValue = cookie("cookieName"); new Tree(...); });

Ce système présente de nombreux avantages par rapport aux espaces de noms imbriqués, car les modules n’ont plus besoin de référencer directement des espaces de noms complets à chaque fois – tout ce dont nous avons besoin est le chemin ‘dojo/cookie’ dans les dépendances, qui une fois aliasé à un argument, peut être référencé par cette variable. Cela supprime le besoin de taper répétitivement ‘dojo.’ dans vos applications.

Note : Bien que Dojo 1.6 ne supporte pas officiellement les modules AMD basés sur l’utilisateur (ni le chargement asynchrone), il est possible de le faire fonctionner avec Dojo en utilisant un certain nombre de chargeurs de script différents. À l’heure actuelle, tous les modules Dojo core et Dijit ont été transformés en syntaxe AMD et une amélioration du support global de l’AMD atterrira probablement entre 1.7 et 2.0.

Le dernier gotcha à connaître est que si vous souhaitez continuer à utiliser le système de construction Dojo ou souhaitez migrer les anciens modules vers ce nouveau style AMD, la version suivante plus verbeuse permet une migration plus facile. Notez que dojo et dijit et référencés comme dépendances aussi:

Patrons de conception de modules AMD (Dojo)

Si vous avez suivi l’un de mes précédents billets sur les avantages des patrons de conception, vous savez qu’ils peuvent être très efficaces pour améliorer la façon dont nous abordons la structuration des solutions aux problèmes de développement courants. John Hann a récemment donné une excellente présentation sur les patrons de conception de modules AMD, couvrant le Singleton, le Decorator, le Mediator et d’autres. Je recommande vivement de consulter ses diapositives si vous en avez l’occasion.

Certains exemples de ces patterns peuvent être trouvés ci-dessous :

P pattern Decorator :

Patron d’adaptateur

Modules AMD avec jQuery

Les bases

À la différence de Dojo, jQuery n’est vraiment livré qu’avec un seul fichier, cependant, étant donné la nature de la bibliothèque basée sur des plugins, nous pouvons démontrer combien il est simple de définir un module AMD qui l’utilise ci-dessous.

Il manque cependant quelque chose à cet exemple et c’est le concept d’enregistrement.

Enregistrer jQuery comme un module asynchrone compatible

L’une des fonctionnalités clés qui a atterri dans jQuery 1.7 était le support pour enregistrer jQuery comme un module asynchrone. Il y a un certain nombre de chargeurs de scripts compatibles (y compris RequireJS et curl) qui sont capables de charger des modules en utilisant un format de module asynchrone et cela signifie que moins de hacks sont nécessaires pour que les choses fonctionnent.

En raison de la popularité de jQuery, les chargeurs AMD doivent prendre en compte plusieurs versions de la bibliothèque qui sont chargées dans la même page car vous ne voulez idéalement pas que plusieurs versions différentes se chargent en même temps. Les chargeurs ont la possibilité de prendre spécifiquement en compte ce problème ou d’indiquer à leurs utilisateurs qu’il existe des problèmes connus avec les scripts tiers et leurs bibliothèques.

Ce que l’ajout de la 1.7 apporte à la table est qu’il permet d’éviter les problèmes avec d’autres codes tiers sur une page chargeant accidentellement une version de jQuery sur la page que le propriétaire n’attendait pas. Vous ne voulez pas que d’autres instances clobber votre propre et donc cela peut être bénéfique.

La façon dont cela fonctionne est que le chargeur de script employé indique qu’il supporte plusieurs versions de jQuery en spécifiant qu’une propriété, define.amd.jQuery est égale à true. Pour ceux qui sont intéressés par des détails de mise en œuvre plus spécifiques, nous enregistrons jQuery en tant que module nommé, car il y a un risque qu’il puisse être concaténé avec d’autres fichiers qui peuvent utiliser la méthode define() d’AMD, mais sans utiliser un script de concaténation approprié qui comprend les définitions de modules anonymes d’AMD.

L’AMD nommé fournit une couverture de sécurité d’être à la fois robuste et sûr pour la plupart des cas d’utilisation.

Des plugins jQuery plus intelligents

J’ai récemment discuté de quelques idées et exemples de la façon dont les plugins jQuery pourraient être écrits en utilisant les modèles de définition de module universel (UMD) ici. Les UMD définissent des modules qui peuvent fonctionner à la fois sur le client et le serveur, ainsi qu’avec tous les chargeurs de script populaires disponibles à l’heure actuelle. Alors que c’est encore un nouveau domaine avec beaucoup de concepts encore en cours de finalisation, n’hésitez pas à regarder les échantillons de code dans le titre de la section AMD && CommonJS ci-dessous et faites-moi savoir si vous pensez qu’il y a quelque chose que nous pourrions améliorer.

Quels chargeurs de scripts &Cadres supportent AMD ?

Dans le navigateur :
Côté serveur :
  • RequireJS http://requirejs.org
  • PINF http://github.com/pinf/loader-js

Conclusions de l’AMD

Les exemples ci-dessus sont des exemples très triviaux de la façon dont les modules de l’AMD peuvent vraiment être utiles, mais ils fournissent, espérons-le, une base pour comprendre comment ils fonctionnent.

Vous serez peut-être intéressé de savoir que de nombreuses grandes applications et entreprises visibles utilisent actuellement des modules AMD dans le cadre de leur architecture. Il s’agit notamment d’IBM et du iPlayer de la BBC, qui soulignent à quel point ce format est sérieusement pris en compte par les développeurs au niveau de l’entreprise.

Pour plus de raisons expliquant pourquoi de nombreux développeurs optent pour l’utilisation des modules AMD dans leurs applications, vous serez peut-être intéressé par ce billet de James Burke.

CommonJS Un format de module optimisé pour le serveur

CommonJS sont un groupe de travail bénévole qui vise à concevoir, prototyper et standardiser les API JavaScript. À ce jour, ils ont tenté de ratifier des normes pour les modules et les paquets. La proposition de module CommonJS spécifie une API simple pour déclarer des modules côté serveur et contrairement à AMD tente de couvrir un ensemble plus large de préoccupations telles que io, filesystem, promises et plus encore.

Mise en route

D’un point de vue structurel, un module CJS est un morceau réutilisable de JavaScript qui exporte des objets spécifiques mis à disposition de tout code dépendant – il n’y a généralement pas de wrappers de fonctions autour de tels modules (vous ne verrez donc pas define utilisé ici par exemple).

À un haut niveau, ils contiennent essentiellement deux parties primaires : une variable libre nommée exports qui contient les objets qu’un module souhaite mettre à la disposition des autres modules et une fonction require que les modules peuvent utiliser pour importer les exportations des autres modules.

Comprendre CJS : require() et exports

Consommation basique des exports

Equivalent AMD du premier exemple CJS

Consommation de dépendances multiples

app.js
bar.js
exports.name = 'bar';
foo.js
require('./bar');exports.helloWorld = function(){ return 'Hello World!!''}

Quels chargeurs & Frameworks supportent CJS?

Dans le navigateur:
  • curl.js http://github.com/unscriptable/curl
  • SproutCore 1.1 http://sproutcore.com
  • PINF http://github.com/pinf/loader-js
  • (et plus)
Côté serveur:

CJS est-il adapté au navigateur ?

Il y a des développeurs qui pensent que CommonJS est mieux adapté au développement côté serveur, ce qui est l’une des raisons pour lesquelles il y a actuellement un niveau de désaccord sur le format qui devrait et sera utilisé comme la norme de facto dans l’âge pré-Harmony allant de l’avant. Certains des arguments contre CJS comprennent une note que de nombreuses API CommonJS adressent des fonctionnalités orientées serveur que l’on ne serait tout simplement pas en mesure d’implémenter au niveau du navigateur en JavaScript – par exemple, io, system et js pourraient être considérés comme non implémentables par la nature de leur fonctionnalité.

Cela dit, il est utile de savoir comment structurer les modules CJS indépendamment afin que nous puissions mieux apprécier comment ils s’intègrent lors de la définition de modules qui peuvent être utilisés partout. Les modules qui ont des applications à la fois sur le client et le serveur comprennent les moteurs de validation, de conversion et de templating. La manière dont certains développeurs abordent le choix du format à utiliser consiste à opter pour CJS lorsqu’un module peut être utilisé dans un environnement côté serveur et à utiliser AMD si ce n’est pas le cas.

Comme les modules AMD sont capables d’utiliser des plugins et peuvent définir des choses plus granulaires comme les constructeurs et les fonctions, cela a du sens. Les modules CJS sont seulement capables de définir des objets qui peuvent être fastidieux à travailler si vous essayez d’obtenir des constructeurs à partir d’eux.

Bien que cela dépasse la portée de cet article, vous avez peut-être aussi remarqué qu’il y avait différents types de méthodes ‘require’ mentionnées lors de la discussion sur AMD et CJS.

Le souci avec une convention de dénomination similaire est bien sûr la confusion et la communauté est actuellement divisée sur les mérites d’une fonction require globale. La suggestion de John Hann ici est que plutôt que de l’appeler ‘require’, ce qui ne parviendrait probablement pas à atteindre l’objectif d’informer les utilisateurs sur la différence entre un require global et un require interne, il pourrait être plus judicieux de renommer la méthode de chargeur global quelque chose d’autre (par exemple le nom de la bibliothèque). C’est pour cette raison qu’un chargeur comme curl.js utilise curl() par opposition à require.

AMD && CommonJS Normes concurrentes, mais tout aussi valables

Alors que cet article a mis davantage l’accent sur l’utilisation d’AMD par rapport à CJS, la réalité est que les deux formats sont valables et ont une utilité.

AMD adopte une approche du développement centrée sur le navigateur, optant pour un comportement asynchrone et une compatibilité ascendante simplifiée, mais il n’a aucun concept d’E/S de fichier. Il prend en charge les objets, les fonctions, les constructeurs, les chaînes de caractères, JSON et de nombreux autres types de modules, fonctionnant nativement dans le navigateur. Il est incroyablement flexible.

CommonJS d’autre part adopte une approche serveur d’abord, en supposant un comportement synchrone, pas de bagage global comme John Hann s’y référerait et il tente de répondre à l’avenir (sur le serveur). Ce que nous voulons dire par là, c’est que parce que CJS supporte les modules non enveloppés, il peut se sentir un peu plus proche des spécifications ES.next/Harmony, en vous libérant du wrapper define() qu’AMD impose. Les modules CJS ne supportent cependant que les objets en tant que modules.

Bien que l’idée d’un autre format de module puisse être décourageante, vous pouvez être intéressé par quelques échantillons de travail sur les modules hybrides AMD/CJS et Univeral AMD/CJS.

Format hybride AMD de base (John Hann)

Définition de module universel AMD/CommonJS (Variation 2, UMDjs)

Plugins UMD extensibles avec (Variation par moi-même et Thomas Davis).

core.js
myExtension.js
;(function ( name, definition ) { var theModule = definition(), hasDefine = typeof define === 'function', hasExports = typeof module !== 'undefined' && module.exports; if ( hasDefine ) { // AMD Module define(theModule); } else if ( hasExports ) { // Node.js Module module.exports = theModule; } else { // Assign to common namespaces or simply the global object (window) // account for for flat-file/global module extensions var obj = null; var namespaces = name.split("."); var scope = (this.jQuery || this.ender || this.$ || this); for (var i = 0; i 
app.js
$(function(){ // the plugin 'core' is exposed under a core namespace in // this example which we first cache var core = $.core; // use then use some of the built-in core functionality to // highlight all divs in the page yellow core.highlightAll(); // access the plugins (extensions) loaded into the 'plugin' // namespace of our core module: // Set the first div in the page to have a green background. core.plugin.setGreen("div:first"); // Here we're making use of the core's 'highlight' method // under the hood from a plugin loaded in after it // Set the last div to the 'errorColor' property defined in // our core module/plugin. If you review the code further down // you'll see how easy it is to consume properties and methods // between the core and other plugins core.plugin.setRed('div:last');});

ES Harmony Modules Of The Future

TC39, l'organisme de normalisation chargé de définir la syntaxe et la sémantique de l'ECMAScript et de ses itérations futures est composé d'un certain nombre de développeurs très intelligents. Certains de ces développeurs (comme Alex Russell) ont suivi de près l'évolution de l'utilisation de JavaScript pour le développement à grande échelle au cours des dernières années et sont parfaitement conscients de la nécessité d'améliorer les caractéristiques du langage pour écrire un JS plus modulaire.

Pour cette raison, il existe actuellement des propositions pour un certain nombre d'ajouts passionnants au langage, y compris des modules flexibles qui peuvent fonctionner à la fois sur le client et le serveur, un chargeur de modules et plus encore. Dans cette section, je vous montrerai quelques échantillons de code de la syntaxe des modules dans ES.next afin que vous puissiez avoir un avant-goût de ce qui est à venir.

Note : Bien que Harmony soit encore dans les phases de proposition, vous pouvez déjà essayer des fonctionnalités (partielles) de ES.next qui abordent le support natif pour l'écriture de JavaScript modulaire grâce au compilateur Traceur de Google. Pour être opérationnel avec Traceur en moins d'une minute, lisez ce guide de démarrage. Il y a également une présentation JSConf à ce sujet qui vaut la peine d'être regardée si vous êtes intéressé à en apprendre davantage sur le projet.

Modules avec les importations et les exportations

Si vous avez lu les sections sur les modules AMD et CJS, vous êtes peut-être familier avec le concept de dépendances de module (importations) et les exportations de module (ou, les API/variables publiques que nous permettons aux autres modules de consommer). Dans ES.next, ces concepts ont été proposés d'une manière légèrement plus succincte, les dépendances étant spécifiées à l'aide d'un mot-clé import. export n'est pas très différent de ce à quoi nous pourrions nous attendre et je pense que de nombreux développeurs regarderont le code ci-dessous et le " comprendront " instantanément.

  • Les déclarations d’importation lient les exportations d’un module en tant que variables locales et peuvent être renommées pour éviter les collisions/conflits de noms.
  • Les déclarations d’exportation déclarent qu’une liaison locale d’un module est visible de l’extérieur de sorte que les autres modules peuvent lire les exportations mais ne peuvent pas les modifier. Il est intéressant de noter que les modules peuvent exporter des modules enfants mais ne peuvent pas exporter des modules qui ont été définis ailleurs. Vous pouvez également renommer les exportations afin que leur nom externe diffère de leur nom local.

Modules chargés à partir de sources distantes

Les propositions de modules prennent également en charge les modules qui sont basés à distance (par exemple, un wrapper API tiers), ce qui rend simpliste le chargement de modules à partir d’emplacements externes. Voici un exemple de nous tirant dans le module que nous avons défini ci-dessus et l’utilisant:

Module Loader API

Le chargeur de module proposé décrit une API dynamique pour charger des modules dans des contextes hautement contrôlés. Les signatures prises en charge par le chargeur comprennent load( url, moduleInstance, error) pour le chargement de modules, createModule( object, globalModuleReferences) et d’autres. Voici un autre exemple où nous chargeons dynamiquement le module que nous avons initialement défini. Notez que, contrairement au dernier exemple où nous avons tiré dans un module à partir d’une source distante, l’API de chargeur de module est mieux adapté aux contextes dynamiques.

Loader.load('http://addyosmani.com/factory/cakes.js', function(cakeFactory){ cakeFactory.oven.makeCupcake('chocolate'); });

Modules de type CommonJS pour le serveur

Pour les développeurs qui sont orientés vers le serveur, le système de module proposé pour ES.next n’est pas seulement contraint à regarder les modules dans le navigateur. Ci-dessous à titre d’exemples, vous pouvez voir un module de type CJS proposé pour une utilisation sur le serveur :

// io/File.jsexport function open(path) { ... };export function close(hnd) { ... };
module lexer from 'compiler/LexicalHandler';module stdlib from '@std'; //... scan(cmdline) ...

Classes Avec Constructeurs, Getters & Setters

La notion de classe a toujours été un sujet de controverse avec les puristes et nous nous sommes jusqu’à présent débrouillés soit en nous rabattant sur la nature prototypique de JavaScript, soit en utilisant des frameworks ou des abstractions qui offrent la possibilité d’utiliser des définitions de classe sous une forme qui se rapproche du même comportement prototypique.

Dans Harmony, les classes viennent comme une partie du langage avec les constructeurs et (enfin) un certain sens de la vraie vie privée. Dans les exemples suivants, j’ai inclus quelques commentaires en ligne pour vous aider à comprendre comment les classes sont structurées, mais vous pouvez également remarquer l’absence du mot  » fonction  » ici. Il ne s’agit pas d’une erreur de frappe : TC39 a fait un effort conscient pour diminuer notre abus du mot clé function pour tout et l’espoir est que cela aidera à simplifier la façon dont nous écrivons le code.

class Cake{ // We can define the body of a class' constructor // function by using the keyword 'constructor' followed // by an argument list of public and private declarations. constructor( name, toppings, price, cakeSize ){ public name = name; public cakeSize = cakeSize; public toppings = toppings; private price = price; } // As a part of ES.next's efforts to decrease the unnecessary // use of 'function' for everything, you'll notice that it's // dropped for cases such as the following. Here an identifier // followed by an argument list and a body defines a new method addTopping( topping ){ public(this).toppings.push(topping); } // Getters can be defined by declaring get before // an identifier/method name and a curly body. get allToppings(){ return public(this).toppings; } get qualifiesForDiscount(){ return private(this).price > 5; } // Similar to getters, setters can be defined by using // the 'set' keyword before an identifier set cakeSize( cSize ){ if( cSize 

ES Harmony Conclusions

As you can see, ES.next is coming with some exciting new additions. Although Traceur can be used to an extent to try our such features in the present, remember that it may not be the best idea to plan out your system to use Harmony (just yet). There are risks here such as specifications changing and a potential failure at the cross-browser level (IE9 for example will take a while to die) so your best bets until we have both spec finalization and coverage are AMD (for in-browser modules) and CJS (for those on the server).

Conclusions And Further Reading A Review

In this article we've reviewed several of the options available for writing modular JavaScript using modern module formats. These formats have a number of advantages over using the (classical) module pattern alone including: avoiding a need for developers to create global variables for each module they create, better support for static and dynamic dependency management, improved compatibility with script loaders, better (optional) compatibility for modules on the server and more.

In short, I recommend trying out what's been suggested today as these formats offer a lot of power and flexibility that can help when building applications based on many reusable blocks of functionality.

And that's it for now. If you have further questions about any of the topics covered today, feel free to hit me up on twitter and I'll do my best to help!

Copyright Addy Osmani, 2012. All Rights Reserved.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.