fr.romain:blog:3.0

Un blog qu'il est bien pour le Java

Devoxx France 2013 - Du JavaScript Propre ? Challenge Accepted

| Comments

Du JavaScript propre ? Challenge Accepted!

Dernier billet sur la conférence Devoxx France 2013 (ouf !). Je vais parler de notre session, Du JavaScript propre ? Challenge Accepted!, présentée avec Julien Jakubowski d’OCTO.

Alors pour la petite histoire, il faut savoir que Julien et moi ne nous connaissions pas personnellement un mois et demi avant Devoxx ! Nous avions tous les deux proposé une conférence durant le CFP (Call For Paper) très similaire. De son côté, Julien avait proposé une version améliorée de sa présentation Le JavaScript ce n’est pas si sale qu’il avait faite au Ch’ti JUG dont il est le fondateur. De mon côté, j’avais proposé à peu près la même chose, mais dont le but était plutôt de faire un état des lieux de l’écosystème autour de JavaScript en 2013, et montrer que celui-ci est extrêmement riche, presque à l’image de celui du monde Java. Le comité de direction, ne sachant qui choisir, nous a demandé s’il était possible de mixer nos présentations. Ce qui fut fait. On est donc parti de la présentation de Julien, puis on l’a améliorée, suite à nos nombreuses discussions. Je pense également que le fait de la présenter à deux la rend plus vivante, plus intéressante.

Le calme avant la tempête

Le contenu

La session se divise en deux parties :

  • Les problèmes courants en JavaScript.
  • Les solutions que l’on propose pour coder proprement en JavaScript.

Les problèmes

Tout d’abord, nous expliquons pourquoi nous faisons cette présentation. Le constat est double :

  • JavaScript est partout, et les sites sont de plus en plus riches en partie grâce à lui. On ne peut donc pas l’ignorer, surtout avec l’avénement du HTML5.
  • JavaScript a une mauvaise réputation. Le langage est sale, plus adapté à la bidouille sur sa page HTML que pour faire de vraies applications, difficulté de trouver un bon environnement de travail.

Bref, il y a du boulot pour redorer le blason de JavaScript. Après une petite partie ludique, où l’on montre quelques résultats étonnants de JavaScript (je vous conseille fortement de regarder cette vidéo), on passe aux vrais problèmes que l’on est susceptible de rencontrer dans ses projets :

  • Pollution de l’espace de nommage. Quand on définit des fonctions dans les fichiers JavaScript sans prendre garde, il se peut qu’il y ait des collisions entre plusieurs fonctions ayant le même nom. Par exemple, si l’on définit une méthode checkForm() dans un fichier JS, rien ne dit que nous n’aurons pas une méthode au même nom dans un autre fichier. Dans pareille situation, JS ne considère que la dernière méthode définie, et cela peut poser des problèmes.
  • Le scope global par défaut. Par défaut, les variables ont un scope global, et cela peut aussi entrainer des problèmes. Dans le code suivant, la variable i sera la même dans les 2 boucles, ce qui fait que nous n’applerons le logger que 10 fois et non 100 fois comme attendu :
1
2
3
4
5
6
7
8
9
function subLoop() {
  for (i = 0; i < 10; i++) {
      console.log(i);
  }
}

for (var i = 0; i < 10; i++) {
  subLoop();
}
  • Tout est public par défaut. Difficile de cacher certains détails de son implémentation, le mot clé private n’existant pas en JS. Dans le code suivant, la variable censée être privée ne l’est pas du tout :
1
2
3
4
5
6
7
8
9
10
11
var counter = {
  privateValue: 0,
  increment: function() {
      counter.privateValue++;
      return counter.privateValue;
  }
}

counter.increment(); // 1
counter.privateValue = 0; // Aie
counter.increment(); // 1
  • Code non testé. Tout est dit :)

Assez parlé des problèmes, voyons les solutions !

Les solutions

Première idée : on pourrait simplement déléguer le code JavaScript à son framework préféré (GWT, JSF, etc.) Nous ne souhaitons pas aller dans cette direction. Faisons du JavaScript ! Autre idée, pour au moins limiter les dégâts : utiliser Google Dart ou CoffeeScript qui permettent une écriture plus fluide, plus sécurisée du code JavaScript.

D’un point de vue architectural, donc assez haut niveau, il existe aujourd’hui une tendance à ne pas négliger : les architectures MVC/MVW/MV* côté client. Autrement dit, plutôt que d’avoir un serveur qui va s’occuper de toute la partie fonctionnelle (couches modèle, contrôleur et vue), on va déporter tout ou partie de ceci côté client. Pour se faire, on se fera aider de solutions telles que Backbone.js, Ember ou le très à la mode AngularJS. Ce type d’architecture offre d’autres avantages : comme le code est exécuté côté client, le temps de réponse est instantané, on transfère beaucoup moins d’informations entre le client et le serveur (ce qui privilégie les réseaux mobiles), et offre plus facilement un mode déconnecté à son application. Mais ce n’est pas magique, il faudra faire attention à d’autres points, en particulier l’aspect sécurité (bah oui, tout le code est sur le client, donc il lui est plus facile de le comprendre et de le contourner).

Et si on déplaçait le code métier du côté client ?

On a cité, parmi les problèmes, le fait que tout est public par défaut. Pour le résoudre, on propose d’opter pour la modularité. Cela revient un peu à simuler une classe en Java (attention, ce n’est pas exactement ça, c’est juste pour faire comprendre l’idée), en n’exposant à l’extérieur que ce que l’on souhaite (une sorte d’interface). Le code de notre compteur devient au final quelque chose comme ça :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Notre counter sera notre objet utilisé par la suite.
var counter = (function() {
  var privateValue = 0;
  var publicMethod = function() {
      privateValue++;
      return privateValue;
  };
  // On retourne un objet qui contient une méthode "increment", seul élément qui sera visible de l'extérieur.
  return {
      increment: publicMethod
  };
})();

// On passe à l'utilisation du compteur :
counter.increment();  // 1
counter.privateValue; // undefined
counter.privateValue = 0; // Cela ne pose pas de problème. JS va ajouter une propriété "privateValue" à l'objet "counter", mais elle sera différente du "privateValue" que nous utilisons dans notre implémentation. Pas de conflit du coup !
counter.increment();  // 2

Pour accroitre encore la lisibilité et la propreté de son code, on pourra créer des sortes de packages pour nos modules :

1
2
3
4
5
6
7
8
9
10
11
12
var myapp = {
  subpackage: {

      counter: (function() {
          // ...
      })()

  }
}

// Et son utilisation :
myapp.subpackage.counter.increment(); // 1

On aborde ensuite la question du chargement des librairies JS. Plutôt que d’avoir 15 fichiers JS chargés au chargement de la page (chargement séquentiel, qui plus est, ce qui ralentit d’autant le chargement), on va utiliser les principes de l’AMD (Asynchronous Modules Definition), par exemple grâce à la librairie require.js. Cela apporte en particulier deux avantages :

  • Chargement asynchrone et/ou en parallèle, et donc cela ne bloque pas le chargement des pages, qui se fait normalement de façon séquentielle.
  • Chargement à la demande. Certaines fonctionnalités de la page peuvent ne pas être utilisées. Alors autant ne pas charger les ressources liées. On pourra ainsi dire “quand l’utilisateur souhaite faire ceci, alors on chargera à ce moment-là les librairies X et Y”.
  • Définition des dépendances. Un peu comme dans le point précédent, on pourra indiquer que la librairie X dépend de Y, et donc on chargera Y avant X.

Intéressons-nous au code en lui-même maintenant. Il existe des outils pour s’assurer que nous n’écrivons pas (trop) de bêtises dans notre code JavaScript, un peu comme le fait Checkstyle ou PMD en Java. Nous avons ainsi JsLint ou JsHint pour remplir cette tâche. On tâchera aussi d’écrire un code plus expressif, c’est-à-dire plus simple, plus lisible, plus concis. J’aime l’expression qui dit qu’il faut écrire le code comme si la personne qui va le maintenir est un psychopathe qui connait mon adresse, ça traduit bien l’intérêt d’écrire correctement son code.

Un vieux code tel que celui-ci :

1
2
3
4
5
6
7
8
9
function checkForm() {
  var beerName = document.getElementById("beer_name").value;
  if (beerName === '') {
      document.getElementById("beer_name").className += "invalid";
      document.getElementById("error_box").style.display = "block";
      return false;
  }
  return true;
}

pourra être refactoré avec jQuery de cette façon, rendant le code plus lisible, plus facile à maintenir :

1
2
3
4
5
6
7
8
$('#beerForm').submit(function() {
  if ($('#beer_name').val() === "") {
      $('#beer_name').addClass('invalid');
      $('#error_box').show();
      return false;
  }
  return true;
});

Autre chose pour simplifier l’écriture : utiliser des moteurs de templating, tels que Mustache.js ou Handlebars.js, qui permettent d’insérer des données dans du HTML de façon fluide. Ainsi, si beers est un tableau de bières (au format JSON), on pourra écrire ceci :

1
2
3
4
5
6
7
8
9
10
<ul id="beers-list">
  {{#beers}}
      <li>{{name}} - {{color}} - {{alcohol}}%</li>
  {{/beers}}
</ul>
<script type="text/javascript">
  var template = $('#beers-list');
  // Appel du moteur de rendu de Mustache
  template.html( Mustache.render(template.html(), data) );
</script>

Autre sujet important, les tests. Nous n’allons pas dans le détail, mais nous expliquons qu’il existe de nombreux outils pour cela : Jasmine, QUnit, Mocha, CapserJS, PhantomJS, Karma, etc. Je vous renvoie à l’université de Jean-Laurent de Morlhon et Pierre Gayvallet à ce sujet.

Le message est clair : tous les outils sont là pour faire des tests, du TDD, du BDD, des tests d’intégration, etc. Bref, on n’a plus d’excuses !

Faites des tests en JavaScript

Dernier point abordé : l’automatisation. On peut facilement intégrer son application JavaScript dans un serveur d’intégration continue comme Jenkins ou un outil d’analyse qualité tel que Sonar. Je vous renvoie à mon Tools In Action de l’année dernière pour voir tout ça en pratique. Niveau IDE, nous recommandons IntelliJ IDEA de JetBrains (ou WebStorm) qui offre un vrai support des langages web comme le JavaScript, le HTML ou le CSS.

La présentation à Devoxx France

Voilà un retour personnel sur la présentation elle-même. Déjà, on était très content avec Julien, car la salle était pleine, ce qui fait 300 personnes à nous écouter (sans compter les millions qui nous verront sur Parleys). Si si, j’avais compté les chaises pendant que la salle était vide ! :o)

Y a du monde à gauche ... et aussi à droite !

On a plutôt bien géré notre temps, on a fini un peu en avance même - environ 8 minutes avant - ce qui nous a permis de prendre près d’une dizaine de questions. Les retours que j’ai eu en direct ou sur le Net semblent montrer que la présentation a été appréciée, et c’est ce qui nous rend encore plus heureux (un exemple ici).

Une chose toutefois, que l’on fera peut-être si notre présentation est prise sur une autre conférence. Il semblerait, d’après les questions, que certaines personnes s’attendaient à avoir un peu la stack idéale pour développer en JavaScript. Tout d’abord, elle n’existe pas. Choisir un framework JavaScript va dépendre de vos besoins, mais aussi de votre maitrise du langage, et du risque que vous acceptez de courrir par rapport à la pérénnité de tel ou tel outil. Je ne conseillerais pas AngularJS ou Backbone.js sans connaitre le contexte. Partir sur une application exécutée côté clent, cela a un impact à plus ou moins long terme, et faire ce choix n’est pas anodin.

L’un des points sur lequel il faut faire attention, c’est l’intégration des outils entre eux. Il n’est pas toujours évident de faire fonctionner X avec Y. Aussi, peut-être proposerons-nous dans une version améliorée de la présentation deux ou trois stacks qui fonctionnent bien ensemble.

Références

  • Les slides sur Slideshare ici ou .
  • Les slides et bientôt la vidéo sur Parleys.

Comments