Guide
du lecteur

Les bonnes pratiques JavaScript selon Google

par
le à 11h53

Cet article est une adaptation du Google JavaScript Style Guide en FR. Je n'applique pas nécessairement moi-même toutes les bonnes pratiques listées ci-dessous mais si vous ne vous êtes jamais posé la question : « Comment maintenir un code gardant l'équilibre entre lisibilité et performance avec mon équipe ou les personnes susceptibles de relire mon code » cet article peut s'avérer intéressant. Si vous avez déjà vos pratiques : c'est peut-être l'occasion (comme pour moi) d'en revoir certaines.

Google JavaScript Style Guide en français

Google JavaScript Style Guide en français

Vous pourrez trouver l'article original Google JavaScript Style Guide derrière ce lien. Au moment où je réalise cette traduction, le document est en version 2.93. À partir de maintenant, tout ce qui suit provient de l'article de Google.

Avant-propos

JavaScript est le principal langage de programmation de la partie cliente d'un navigateur web utilisé par beaucoup de projets Google open-source. Ce guide de style représente la liste des bonnes et des mauvaises pratiques lors d'un développement JavaScript.

Règles du langage JavaScript

var

Déclarations des variables avec var : Toujours - En savoir plus

Quand vous ne spécifiez pas var, la variable est attachée au contexte global de l'application, venant écraser des valeurs de variables potentiellement existantes. De la même manière, sans cette déclaration, il est difficile de définir à quelle portée lexicale la variable est rattachée (ex : La variable pourrait tout aussi bien être attachée à document, à window ou à la fonction courante). Donc n'oubliez jamais de déclarer avec var.

Constante

  • Utiliser des NAMES_LIKE_THIS comme nom de variable pour les valeurs constantes.
  • Utiliser @const (en commentaire) pour indiquer la non réaffectabilité d'une variable (une variable ou une propriété).
  • Ne jamais utiliser le mot clé const car il n'est pas supporté par Internet Explorer.

En savoir plus

Valeurs constantes

Si la valeur d'une variable doit restée constante et inchangeable tout au long de l'exécution du programme, on peut lui donner un nom tel que CONSTANT_VALUE_CASE. Les variable nommées en ALL_CAPS impliquent donc le commentaire @const (car la valeur n'est pas réaffectable).

Les types primitifs (Number, String et Boolean) peuvent être des valeurs constantes.

La constance d'un type Object est plus subjective — Les objets peuvent être considérés comme constant uniquement s'ils ne montrent aucun signe de changement d'état. Cela n'est pas nécésairement triviale à appréhender pour un compilateur.

Pointeurs constants (variables et propriétés)

La notation @const en commentaire d'une variable ou d'une propriété implique qu'elle ne sera pas réaffectable. Ceci est appliquable par un compilateur lors de la compilation. Cette convention est cohérente avec le mot-clé const (que nous n'utilisons pas en raison du manque de soutien dans Internet Explorer).

La notation @const sur une méthode implique que cette fonction ne sera pas héritable par des sous classes.

La notation @const sur un constructeur implique que cette classe ne pourra pas être hérité (comme le final de Java).

Exemples

A noter que @const n'implique pas nécéssairement une écriture de variable comme celle-ci : CONSTANT_VALUES_CASE. Cependant, écrire une variable comme cela CONSTANT_VALUES_CASE implique @const.

/**
 * Temps maximal pour la Demande en millisecondes.
 * @type {number}
 */
goog.example.TIMEOUT_IN_MILLISECONDS = 60 * 1000;

Le nombre de secondes dans une minute ne change jamais. C'est une valeur constante. L'écriture ALL_CAPS requière le commentaire @const, et donc la constante ne peut pas être réaffectée.

Les compilateurs open-source permettront à une variable d'être réaffectée si elle n'est pas marquée comme @const.

/**
 * URL associée à la Carte en tant que chaine de caractères.
 * @const
 */
MyClass.fetchedUrlCache_ = new goog.structs.Map();

/**
 * La Classe ne peut pas être héritée.
 * @const
 * @constructor
 */
sloth.MyFinalClass = function() {};

Dans ce cas, le pointeur ne sera jamais réaffectée, cependant il peut changer d'un appel à l'autre (C'est pourquoi il est écrit en camelCase, et non en ALL_CAPS).

Point-virgule

Toujours utiliser les point-virgules - En savoir plus

Une utilisation implicite des point-virgules (ne pas les mettre) pose problème, des problèmes difficiles à debboguer. Ne le faites pas. Ça vaudra mieux pour vous.

Il y a quelques endroits ou l'oubli de point-virgule est particulièrement dangereux :

MyClass.prototype.myMethod = function() {
  return 42;
}  // Pas de point-virgule ici.

(function() {
  // Du code à exécuter ici et isolé dans un champ lexical localisé.
})();

Quel est le problème ?

Erreur JavaScript - Tout d'abord la première fonction retourne 42 puis est appelé en tant que paramètre par la seconde fonction, quand 42 est appelé cela cause une erreur.

var x = {
  'i': 1,
  'j': 2
}  // Pas de point-virgule ici.

// Essayons de faire une chose sur Internet Explorer et une autre sur Firefox.
// Je sais que vous ne devez pas écrire du code comme ça, et que c'est un peu tiré par les cheveux.
[normalVersion, ffVersion][isIE]();

Quel est le problème ?

Vous aurez probablement une erreur comme 'no such property in undefined' à l'exécution car il essayera d'appeler x[ffVersion][isIE]().

var THINGS_TO_EAT = [apples, oysters, sprayOnCheese] // Pas de point-virgule ici.

// Exécution conditionnelle a la bash.
-1 == resultOfOperation() || die();

Quel est le problème ?

die est exécuté à moins que resultOfOperation() ai comme valeur NaN et THINGS_TO_EAT sera assigné par le résultat de la fonction die().

Pourquoi ?

JavaScript a besoin d'un point-virgule à la fin des instructions, sauf quand il peut être sur et certain de deviner les fins d'instructions. Dans chacun des exemples précédents, une déclaration de fonction ou de litéral objet/tableau (écriture au format JSON) est utilisé près d'une instruction. Les crochets de fermeture ne sont pas assez pour deviner la fin d'une instruction. Javascript ne termine jamais une instruction si le caractère suivant est un opérateur de fermeture.

Cela surprend pas mal de gens, aussi soyez sur de ne pas oublier de point-virgules dans vos travaux.

Clarification : point-virgules et fonctions

Les point-virgules doivent être placé à la fin d'une affectation de fonction, mais pas à la fin d'une déclaration de fonction. Cette distinction est facilement illustrée avec l'exemple suivant :

var foo = function() {
  return true;
};  // Point-virgule ici.

function foo() {
  return true;
}  // Pas de point-virgule ici.

Fonction imbriquée

Oui - En savoir plus

Les fonctions imbriquées peuvent s'avérer très utiles, par exemple en ce qui concerne la portée des fonctions ou pour cloisonner des fonctions d'aide au débogguage ou développement. Utilisez les sans retenu.

Déclaration de fonction dans un block

Non - En savoir plus

Ne faites pas ça :

if (x) {
  function foo() {}
}

Bien que la plupart des moteurs d'exécution JavaScript supporte les Déclaration de fonction dans les blocks, cela ne fait pas parti des spécifications ECMAScript (voir ECMA-262, partie 13 et 14). Ce type d'implémentation peut donc s'avérer incompatible avec d'autre implémentation ou avec des propositons futures d'évolution des normes ECMAScript. ECMAScript autorise les déclarations de fonction uniquement dans le contexte globale (la racine des scripts) ou celui d'une fonction. À la place, il faut utiliser l'assignation de fonction dans une variable pour créer une fonction dans un block :

if (x) {
  var foo = function() {}
}

Exceptions

Oui - En savoir plus

Vous ne pouvez tout simplement pas éviter les exceptions si vous faites quelque chose de non-trivial (à l'aide d'un Framework, etc...). Allez-y.

Exceptions personalisées

Oui - En savoir plus

Sans exceptions personalisées, retourner les informations d'erreur d'une fonction qui elle même retourne une valeur est assez délicat, pour ne pas dire bancale. Les mauvaise solutions sont de passer une variable erreur pour récupérer celle-ci ou d'avoir une propriété dans un objet passé destiné à accueillir l'erreur. Cela s'apparente à un détournement de l'exception de base. Il est donc possible d'utiliser des exceptions customisées quand cela est nécessaire.

Fonctionalités standards

Toujours préférer des fonctionalités standards à des fonctionalités non-standards - En savoir plus

Pour une portabilité et une compatibilité maximum, il faut toujours préférer des fonctionalités standards à des fonctionalités non-standards (ex : string.charAt(3) à la place de string[3] ou l'accès au DOM avec fonctions standards au lieu d'utiliser un raccourci spécifique au compilateur).

Le type Object des types primifs

Non - En savoir plus

Il n'y a pas de raison d'utiliser le type Object correspondant à un type primitif, en plus cela est dangereux :

var x = new Boolean(false);
if (x) {
  alert('hi');  // Affiche 'hi' quand même.
}

Ne faites pas ça !

Cependant le cast d'objet par cette méthode est parfais (pas de new).

var x = Boolean(0);
if (x) {
  alert('hi');  // Ce message ne s'affichera jamais.
}
typeof Boolean(0) == 'boolean'; // Bon.
typeof new Boolean(0) == 'object'; // Pas sécurisé.

Cela est très utile pour caster les types number, boolean et string.

Hiérarchie multi-niveau par prototype

Pas recommandé - En savoir plus

La Hiérarchie multi-niveau par prototype est la manière dont le JavaScript gère l'héritage. Vous avez une hierarchie multi-niveau quand une fonction définie D est complètement inclue dans une fonction définie B via leurs prototype. Cette hierarchisation est plus difficile à maintenir qu'il n'y parait !

Pour cette raison, il vaut mieux utiliser goog.inherits() de la Closure Library ou quelque chose de similaire.

function D() {
  goog.base(this)
}
goog.inherits(D, B);

D.prototype.method = function() {
  ...
};

Déclaration de méthode et de propriété

/** @constructor */ function SomeConstructor() { this.someProperty = 1; } Foo.prototype.someMethod = function() { ... }; - En savoir plus

Bien qu'il y ai plusieurs manière d'attacher des méthodes et propriétés à un objet créer via new, il vaut mieux préférer :

Foo.prototype.bar = function() {
  /* ... */
};

La meilleure manière de procéder pour les autres initialisations est celle effectuée dans le constructeur en tant qu'attribut :

/** @constructor */
function Foo() {
  this.bar = value;
}

Pourquoi ?

Les moteurs JavaScript actuelle ont une optimisation basée sur la "forme" d'un objet ; ajouter une propriété à un objet (également d'écraser une valeur incluse dans le prototype) change la forme et dégrade les performances.

delete

Préferez this.foo = null - En savoir plus

Foo.prototype.dispose = function() {
  this.property_ = null;
};

à la place de

Foo.prototype.dispose = function() {
  delete this.property_;
};

Dans les moteurs JavaScript moderne, changer le nombre de propriété d'un objet est plus lent que de réassigner une valeur. Le mot clé delete doit être évité sauf s'il est nécésaire de détruire une propriété d'un objet itératif avec une liste de clé/valeur, ou pour changer son nom (le nom de la clé dans l'objet).

Closures

Oui mais attention - En savoir plus

La capacité de créer des champs lexicaux ou contexte d'exécution persistant (closure) est la fonctionalité la plus utile et la plus étudiée de JS. Vous trouverez ici une bonne description de comment fonctionne les closures.

Une chose qu'il faut cependant garder à l'esprit est qu'un contexte d'exécution garde un pointeur sur le champ lexical qu'il englobe. Il en résulte que, attacher un contexte d'exécution à un élément du DOM peut créer des références circulaire et une fuite de mémoire. Prenons l'exemple de code suivant comme exemple :

function foo(element, a, b) {
  element.onclick = function() { /* utiliser ici a et b */ };
}

Le contexte d'exécution de la fonction garde une référence sur a, et b même s'ils ne sont jamais utilisé. Cependant les éléments eux-mêmes (dans la fonction interne) garde une référence au contexte d'exécution parent, nous avons donc une boucle qui ne sera jamais nettoyée par le récupérateur de mémoire. Une telle situation doit plutôt être développée de cette manière, avec la structure suivante :

function foo(element, a, b) {
  element.onclick = bar(a, b);
}

function bar(a, b) {
  return function() { /* utiliser a et b */ }
}

eval()

Seulement pour la désérialisation (ex: Pour l'évaluation de réponses RPC) - En savoir plus

eval() rend la sémantique du code confuse et est dangereux à utiliser quand il contient des valeurs entré par un utilisateur. Il y a généralement un meilleur moyen, plus clair et plus sécurisé d'écrire le même code aussi son utilisation n'est pas autorisée.

Pour les réponses RPC vour pouvez utiliser l'objet JSON et lire le résultat avec JSON.parse() à la place de eval().

Nous avons par exemple un serveur qui retourne quelque chose comme ça :

{
  "name": "Alice",
  "id": 31502,
  "email": "looking_glass@example.com"
}
var userInfo = eval(feed);
var email = userInfo['email'];

Si le flux retourné inclut du code JavaScript malicieux, quand il sera évalué grâce à eval(), ce code sera exécuté.

var userInfo = JSON.parse(feed);
var email = userInfo['email'];

Avec JSON.parse() un format JSON invalide (incluant du code executable JavaScript) jettera une exeption.

with() {}

Non - En savoir plus

Utiliser with va rendre obscure la sémantique de votre programme. Cela parceque l'objet passé en paramètre à with peut avoir des propriétés pouvant entré en conflit avec les variables locales à with, cela peut radicalement changer le fonctionnement de votre programme. Par exemple, que fait ce code ?

with (foo) {
  var x = 3;
  return x;
}

Réponse : rien. La variable locale x peut-être mis à mal par une des propriétes de foo, et peut-être même une propriété d'assignement, qui dans notre cas asignerait 3 à x et pourrait produire un comportement aberant.

this

Seulement dans une fonction constructeur, méthodes, et pour la persistance du champ lexical - En savoir plus

Le sens de this peut-être différent en fonction des situations. Parfois, il fait référence au contexte d'exécution d'un objet (dans la plupard des cas), le contexte de l'appelant (dans eval()), un noeud dans le DOM (quand il est utilisé dans un attribut d'évènement HTML), un objet instancié (constructeur) ou dans d'autre objets (si la fontion est appelé avec call() ou apply().

Parcequ'il est facile de s'emêler quand à la signification du this, sont utilisation doit se limiter à :

  • dans les constructeurs
  • dans les méthodes des objets (incluant la création de champs lexicaux persistant)

Boucle for-in

Seulement pour les itérations avec clés sur : object/map/hash - En savoir plus

Les boucles for-in sont souvent utilisés à tord avec les Array. Cette erreur vient du fait que la boucle ne s'effectut plus de l'index 0 à l'index length - 1 mais en lisant les clés des objets et des chaines de prototypes. Il y a donc quelques cas ou ce parcourt ne fonctionne pas :

function printArray(arr) {
  for (var key in arr) {
    print(arr[key]);
  }
}

printArray([0,1,2,3]);  // Ceci marche.

var a = new Array(10);
printArray(a);  // Ceci ne marche pas.

a = document.getElementsByTagName('*');
printArray(a);  // Ceci ne marche pas.

a = [0,1,2,3];
a.buhu = 'wine';
printArray(a);  // Ceci ne marche pas.

a = new Array;
a[3] = 3;
printArray(a);  // Ceci ne marche pas.

Toujours utiliser des boucles normales pour parcourrir les tableaux :

function printArray(arr) {
  var l = arr.length;
  for (var i = 0; i < l; i++) {
    print(arr[i]);
  }
}

Les tableaux associatifs

Ne jamais utiliser un Array pour des tableaux de type : map/hash/associatif - En savoir plus

Les tableaux associatifs ne sont pas permis... ou du moins vous ne pouvez pas utiliser des clés d'index autre que des nombres dans les tableaux. Si vous avez besoin de map/hash : utiliser un Object plutôt qu'un Array car les fonctionalités dont vous avez besoin sont celles d'un objet et non celles d'un tableau. Les Array héritent seulement des Object (comme tous les autres objets en JS que vous pouvez utiliser comme Date, RegExp et String).

Déclaration de String sur plusieurs lignes

Non - En savoir plus

Ne faites pas ça :

var myString = 'A rather long string of English text, an error message \
                actually that just keeps going and going -- an error \
                message to make the Energizer bunny blush (right through \
                those Schwarzenegger shades)! Where was I? Oh yes, \
                you\'ve got an error and all the extraneous whitespace is \
                just gravy.  Have a nice day.';

Les espaces blancs en début de chaque ligne ne peuvent pas être retirés au moment de la compilation. Les espaces blancs après le Backslash conduit à des erreurs difficiles à débogguer ; de plus plusieurs moteur d'exécution JavaScript ne le supporte pas car effectivement cela ne fait pas parti des spécifications ECMAScript.

Utiliser la concaténation de chaine à la place :

var myString = 'A rather long string of English text, an error message ' +
    'actually that just keeps going and going -- an error ' +
    'message to make the Energizer bunny blush (right through ' +
    'those Schwarzenegger shades)! Where was I? Oh yes, ' +
    'you\'ve got an error and all the extraneous whitespace is ' +
    'just gravy.  Have a nice day.';

Écriture litérale des Array et Object (JSON)

Oui - En savoir plus

Utiliser la notation litérale (JSON) pour les constructeurs d'Array et d'Object

Les constructeurs de tableau sont source d'erreur à cause du passage des arguments.

// La taille est 3.
var a1 = new Array(x1, x2, x3);

// La taille est 2.
var a2 = new Array(x1, x2);

// Si x1 est un nombre et un entier naturel la taille sera celle de x1.
// Si x1 est un nombre mais n'est pas un entier naturel cela renverra une exception.

// De plus le tableau peut ne contenir qu'un élément avec x1 en tant que valeur.
var a3 = new Array(x1);

// La taille est 0.
var a4 = new Array();

A cause de cela, si quelqu'un change le code et passe 1 argument au lieu de 2, le tableau ne changera pas de taille.

Pour éviter ce genre de problèmes et cas étrange, utiliser toujours la version litérale.

var a = [x1, x2, x3];
var a2 = [x1, x2];
var a3 = [x1];
var a4 = [];

Les constructeur d'objet n'ont pas le même problème : mais pour la lisibilité et la cohérance l'écriture litérale pour les objets est utilisé.

var o = new Object();

var o2 = new Object();
o2.a = 0;
o2.b = 1;
o2.c = 2;
o2['strange key'] = 3;

Peut-être écrit :

var o = {};

var o2 = {
  a: 0,
  b: 1,
  c: 2,
  'strange key': 3
};

Modifier le prototype des objets natifs

Non - En savoir plus

Modifier les objets natifs comme Object.prototype et Array.prototype est strictement interdit. Modifier d'autres objets natifs comme Function.prototype est moins dangereux mais toujours difficile à déboguer en production et doit être évité.

Les commantaires conditionnels d'Internet Explorer

Non - En savoir plus

Ne faites pas :

var f = function () {
    /*@cc_on if (@_jscript) { return 2* @*/  3; /*@ } @*/
};

Les commentaires conditionnels IE peuvent empêcher les outils automatiques de fonctionner correctement.

Convention de nommage et commentaires JavaScript

Cette partie sera traitée dans un second article vers lequel je mettrai un lien ici prochainement.

0 Commentaire

Choisir un Avatar depuis une url

Adresse : (64px x 64px)
L'url de votre image est incorrecte !

ou Changer la couleur de fond

Nouvelle couleur !

et Choisir un Avatar dans la liste

ChangerFermer
Je crois que vous avez oublié votre commentaire !
Votre email est bizarre !
L'url de votre site doit ressembler à ça : http(s)://(www.)votresite.ici !
Vous ne pouvez pas laisser d'email en restant Anonyme !
Vous ne pouvez pas laisser de site web sans votre email !

Votre commentaire a été ajouter ci-dessous ! Si vous désirez le supprimer ultérieurement, servez vous du code suivant :

Les commentaires sont actuellement affiché du plus rescent au plus ancien. Vous pouvez inverser l'ordre en cliquant ci-dessous :

  • Du plus rescent au plus ancien
  • Du plus ancien au plus rescent

Soyez le premier à laisser un commentaire !

Supprimer
Votre commentaire a été supprimé.
A l'instant

Je trouve ce commentaire pertinent ?

Vous avez aimé cette article ? Commentez le ou .

A propos de l'auteur

Bonjour, je suis Bruno Lesieur, architecte web basé sur Annecy. Intégrateur HTML5, CSS3 et JavaScript ou encore développeur Front-End ; appelez moi comme vous le voulez mais sachez que je suis très friand de performance web, de Responsive Web Design, d'accessibilité web, de bonnes pratiques, de techniques et d'outils de gestion web, de factorisation et d'automatisation, de partage et casse-tête en tout genre ainsi que de boissons gazeuses.
Article Commentaire Avis 0
les-bonnes-pratiques-javascript-selon-google

Quelques #IDet beaucoup de .CLASS

Année
Mois
http://blog.haeresis.fr default fr FR Janvier Février Mars Avril Mai Juin Juillet Août Septembre Octobre Novembre Décembre