:::: MENU ::::

Guide de bonnes pratiques Javascript et règles de codage pour l’entreprise

J’avais déjà écrit quelque chose sur le sujet il y a 2 ans et bien que les bases restent les mêmes, le langage évolue et se répand de plus en plus, notamment au sein des entreprises. Les bases de codes deviennent complexes et là ou on pouvait avant se permettre des erreurs, elles posent aujourd’hui de nombreuses difficultés aussi bien en terme de maintenance, de performance que de sécurité.

Ce guide regroupe les principales bonnes pratiques à appliquer lorsqu’on développe en javascript. Il est conçu autour de règles afin d’être utilisé en temps que référence, par exemple pour fixer le cadre d’un projet. Leur nombre est volontairement limité mais ce sont selon moi les plus importantes à retenir et elles sont hélas souvent mal appliquées, généralement par méconnaissance du langage.

N’hésitez pas à l’utiliser, le copier, le modifier. J’accepte bien évidemment les retours et commentaires avec plaisir.

Règles Générales

L’usage de variables globales doit être évité

Description : On distingue comme variable l’ensemble des types de données y compris les fonctions. Les variables déclarées globales ont une portée ( scope ) sur toute l’application et peuvent réécrire ou se faire réécrire par d’autres parties du code.

Justification : Améliore la maintenance et réduit le couplage entre les différentes parties d’une application

Les variables locales doivent être déclarées à l’aide du mot clé var

Description : Empêche la déclaration d’une variable locale dans l’environnement global . Cette règle découle de la précédente.

Justification : Maîtrise de la portée des variables

La base doit toujours être specifiée lors de l’usage de parseInt

Description : parseInt() accepte comme second argument la base de conversion. Définir cette base permet d’éviter des erreurs, en particulier si la chaine commence par « 0 » ce qui aura pour effet une conversion en base 8 (octale)

Justification : Améliore la sécurité du code

Le mode strict doit être utilisé

Description : Depuis la version 5 d’Ecmascript ( Javascript 1.8.5), il est possible d’utiliser la directive « use strict » pour forcer l’exécution du code en mode strict. Ce mode permet de remonter des erreurs qui sont par défaut silencieuses .

Commentaire : Grâce à ce mode, on évite par exemple l’oublie du mot clé var .

Justification : Le javascript étant un langage assez permissif, le mode strict augmente la sécurité du code.

L’usage de eval() doit être évité

Description : eval() permet de parser et d’évaluer une expression à travers une nouvelle instance de l’interpréteur javascript. Cette fonction est coûteuse en terme de performances mais peut surtout devenir une faille de sécurité importante.

Justification : Améliore la sécurité et les performances du code

Un analyseur de code devrait être utilisé pendant le développement

Description : L’utilisation d’un outil d’analyse syntaxique de code ( linter) du type JsLint dans le processus de développement permet de vérifier le code afin d’éviter des erreurs ou omissions pouvant provoquer des dysfonctionnements et des difficultés à déboguer.

Justification : Augmente la qualité du code

Les fichiers et / ou principales fonctionnalités d’un programme devraient être enrobées dans un module

Description : Cette règle rejoint la première règle à propos des variables globales. La portée d’un module étant limitée, on obtient une plus grande maîtrise du code et on évite aussi la propagation de variables à d’autres fonctions. On peut utiliser plusieurs méthodes pour y parvenir : AMD , CommonJS, ES6, objet en notation littérale, module pattern (IIFE).

Exemple :


// Cette exemple montre l'utilisation d'une IIFE pour réaliser un module
var monModule = (function ( jQ ) {

 function methodePrivee(){
   jQ(".content").html("hello");
 }

 function methodePrivee2(){
   console.log("hello");
 }

 return{
   methodePublique: function(){
   methodePrivee();
 }
 };

// Exemple d'import d'un objet
})( jQuery);

monModule.methodePublique();

Règles de syntaxe

La notation littérale doit être utilisée pour créer des objets, tableaux, expressions régulières et primitives

Description : La notion littérale permet d’éviter de réécrire un objet original en cas d’omission de l’opérateur new.

Justification : Sécurise le code.


// ecrire

var monObjet = {} ;

var monTableau = [] ;

// Plutot que

var monObjet = new Object() ;

var monTableau = new Array() ;

Par défaut, l’égalité strict doit être utilisée

Description : La strict égalité ( === )  permet de vérifier à la fois la valeur et le type. Dans le cas de la simple égalité ( == ), l’interpréteur essayera de faire correspondre le type des éléments comparés ( coercition).

Exemple :


 var a1 = ('1' == 1); // true
 var a2 = ('1' === 1); // false
 var a3 = ('true' == true); // true
 var a4 = ('true' === true); // false

Justification : Permet une plus grande maîtrise lors des comparaisons et évite des résultats non souhaités suite à une conversion de type automatique.

Une expression doit toujours se terminer par un point-virgule

Description : Même si la syntaxe ne l’oblige pas, cette pratique limite le risque d’erreurs, permet la minification et une meilleur analyse de la qualité du code.

Commentaire : L’ajout du point-virgule n’est pas obligatoire en javascript du fait de son insertion automatique par l’interpréteur (ASI) ( cf ECMA-262 v5.1  sect7.9 )

Une condition doit toujours utiliser des accolades

Description : délimiter une instruction par un bloc ( {} ) évite permet de clarifier l’instruction à exécuter pour le développeur ainsi que pour l’interpréteur.

Commentaire : Tout comme le point virgule, javascript tolère l’absence d’accolades pour une déclaration d’instructions  ( cf ECMA-262 v5.1 sect-12 )

Justification : sécurise le code et limite les difficultés de débogage

 

Règles appliquées au javascript coté navigateur

Les scripts javascript doivent être placés dans des fichiers externes

Description : Isoler le javascript dans des fichiers séparés permet de correctement distinguer le code source du corps de page HTML.

Justification : Meilleur maintenabilité.

Les scripts javascript doivent être appelés de préférence en fin de page

Description :  L’interprétation d’un fichier HTML par le navigateur est linéaire et bloquante, de cet fait, appeler les fichiers sources en bas de page rend l’affichage initial de la page plus rapide et permet d’éviter l’appel à des événements de type onReady.

Commentaire : Dans le cas d’un code qui doit interagir avec la page avant l’affichage complet du DOM, on pourra placer ce script dans l’en-tete.

Justification : Améliore la lisibilité et augmentation des performances.

Les sélecteurs doivent être mis en cache lors d’un usage répété

Description : l’interaction avec le DOM est lente. Lors de l’usage répété d’un sélecteur, il convient de le mettre en cache pour éviter de retraverser le DOM.

Exemple :


// Mauvais
for (var i = 0; i < 10000; i++) {
    $('#container').html(i);
}

// Bon
var monElement = $('#container')

for (var i = 0; i < 10000; i++) {
    monElement.html(i);
}

//Meilleur ( usage d'un sélecteur natif ) 
var monElement = document.getElementById(container)

for (var i = 0; i < 10000; i++) {
    monElement.innerHTML = i;
}

Justification : Augmente les performances

Lorsque cela est possible, les fonctions natives du DOM doivent être utilisées

Description : Les navigateurs ont atteint une maturité qui permet de se passer de bibliothèques spécialisées dans la plupart des cas courants (sélection d’éléments, interaction avec le DOM,  appels AJAX)

Commentaire : C’est particulièrement vrai pour la sélection d’éléments du DOM. L’usage d’une bibliothèque externe telle que jQuery uniquement dans ce but est inutile aujourd’hui.

Justification : Limite la complexité du code et la dépendance à des bibliothèques externes

Le code javascript devrait être séparé des balises HTML

Description : Afin de ne pas mélanger les langages , préférer l’usage de sélecteurs pour affecter des événements ou interagir avec des éléments du DOM.

Justification : Meilleur maintenabilité du code.

Commentaire : ceci est aussi valable pour l’attribut style

Exemple :


// Appel de fonction directement dans le HTML ( inline) 
<div id="monElement" onclick="mafonction()">

// Depuis le code Javascript, affectation d'une fonction sur un attribut d'un élément sélectionné 
var monElement = document.getElementById("monElement");

monElement.onclick=function(){...};

// Utilisation du gestionnaire d’événement ( permet l'ajout de plusieurs écouteurs sur un même évenement)
monElement.addEventListener("click", function() {

  ...

});

La fonction document.write() ne devrait pas être utilisée

Description : Cette fonction modifie le DOM pendant la lecture de la page par le navigateur. Cela peut porter à confusion et potentiellement bloquer le rendu de la page. Il est préférable d’utiliser des fonctions de manipulation du DOM une fois la page totalement chargée. Son usage doit être limité à des cas particuliers.

Justification : Améliore la maintenabilité et la compréhension du code

Ressources

Linters

  • JSLint ( le plus strict )
  • JSHint ( meilleur compromis )
  • ESLint ( hautement configurable )

La spécification Ecmascript 5.1

Traduction en français du guide de style javascript de AirBnb

Le guide de style et bonnes pratiques javascript chez Google


3 Comments

  • Répondre Nicolas Gilis |

    Merci pour ton blog que j’ai découvert il y a une semaine, j’ai dévoré tout les articles.

    Par contre 2 questions :

    – pour ce qui est de l’exemple de la boucle n’est-ce pas plus intéressent de déclarer la conditions d’existance dans la boucle histoire de limiter son scope ? -> for(var i = 0, t=array.length; i <= t; i++) ?

    – pour le point "Les scripts javascript doivent être appelés de préférence en fin de page" n'est-ce pas une pratique désuète ? Je suis toujours en phase de test mais je vois de plus en plus d'articles aller dans ce sens. Notamment à cause de la gestion des ressources d'IOS à ce niveau (http://www.24joursdeweb.fr/2014/le-web-mobile-et-la-performance/).

    Merci d'avance

    • Répondre maxdow |

      Bonjour et merci pour ton commentaire ;)

      Pour répondre à tes questions :

      1 : Tu fais référence à cet article sur les boucles ? Je n’insiste pas sur la mise en cache d’un tableau mais je l’évoque dans le paragraphe sur le for. Ta question concerne t’elle le scope des variables ou les performances . Si c’est pour le scope seul, que tu définisses ou non ta variable à l’intérieur du for n’y changera rien, elle sera accessible à l’extérieure du bloc. La seule solution pour limiter efficacement le scope de tes variables vient avec let en ES6. Pour le coup si tu fais for (let i=0,n=10;i<n ….) tes variables i et n ne seront pas visibles hors du for.
      Pour les perfs entre let et var je ne sais pas si ça change quelques chose, à mon avis ça ne peut que être mieux avec let.
      Si on revient plus en détail sur les performances du for et de la mise en cache de la taille du tableau..C'est une bonne question que je me suis longtemps posée. Avant je n'utilisais par défaut que du for caché puis je me suis mis au forEach et maintenant au map/reduce/.. . Qu'est ce qui a changé ? la maintenabilité et la lisibilité de mon code ! ( et je suis quelqu'un qui aime optimiser ;) )
      Gagner 20ns sur des opérations de boucles, généralement , on s'en fiche. Surtout que les moteurs JS étant de plus en plus optimisés cela peu devenir contre productif. Dans le cas de V8 par exemple, si length est une propriété qui ne varie pas, le compilateur la met en cache tout seul. On ne gagne rien si ce n'est une initialisation de variable inutile. Ce qu'il faut c'est mesurer et comparer si le besoin se fait sentir, sinon il vaut mieux préférer la maintenabilité du code . Le forEach est terriblement lent comparé à un for …sur un processeur de 8Mhz !
      En résumé, je pense qu'il faut privilégier la qualité de code à sa performance immédiate. Une fois qu'on a quelque chose de propre et/ou si le besoin existe, alors il faut mettre en place des tests et bonnes pratiques pour améliorer les performances.

      2 : je ne connaissais pas la problématique iOS pour l'appel des script. Je trouve ça un peu dommage! à quoi bon avoir des requêtes asynchrones si c'est pour ne pas afficher la page ? Je reste affirmatif sur le chargement en pas de page. Ne serait ce que pour éviter des document.onReady non maîtrisés que je croise sans cesse ! Si le problème n'existe QUE sur iOS c'est un peu comme refaire internet pour les incompatibilités d'IE6. Je pense qu'il faut rester cohérent aux standards ou à l'utilisation la plus répandue, ensuite adapter à la cible ( appli mobile, desktop ? ) puis au cas par cas ( d'où le "de préférence").

So, what do you think ?