Mise à jour (6 mars) : Un tuto existe à cette adresse : Bonnes pratiques javascript. Il est mieux que ce post
Bonjour chers zero javascripteurs.
Je me lasse un peu de répeter tout le temps la même chose alors je fais un petit post (a rajouter à la faq ou quelque part ça serai génial). J'fais pas de tuto, pas des masses de gens font l'effort de chercher.
Le but est d'en finir avec la programation des années 90 du javascript. Ça «marchait» «bien» avant. Il est grand temps de se remettre à jour et d'arreter de maltraiter le language. Donc on va revoir les bases de chez bases, les bonnes pratiques et enfin des liens pour bien apprendre.
C'est le minimum vital, je suis prêt à débattre et expliquer. C'est pas la bible, c'est pas exhaustif et j'ai pas la science infuse mais, par ici, j'ai plus d'expérience que beaucoup.
Des précisions «a porté locale» à ce post :
j'oublie le XHTML. Pour les pages web, c'est mort depuis le début (merci IE). Il faut noter qu'il y a des différences avec ce que je dis quand on considère du XHTML (beaucoup même),
en javascript il y a toujours beaucoup de de façons de faire les choses, mais il faut savoir justifier ses choix. Si vous savez pas, utilisez les sans broncher, ou demandez
Il faut que ça marche avec IE. C'est du javascript de la «vraie vie», couper les cheuveux en 4 ne sert a rien face à un utilisateur énervé…
Les trucs «qu'on fait jamais gaffe»
Le contexte
Un truc super important mais que l'on ignore lamentablement.
Tout ce qui se trouve dans les balises <script></script> ou <script src="fichier.js"></script> c'est du javascript tout ce qu'il y a de plus classique.
Le javacript dans le HTML c'est le mal, franchement, évitez. Il faut utiliser le DOM et compagnie pour trouver des élements html et rajouter des gestionaires d'évènements (onclick, onmouseover et compagnie). Mais il arrive que ça soit absolument necessaire. Dans ce cas il y a quelques petites choses à suivre.
Les attributs dans le html peuvent être en majuscules ou minuscule (dans le 5 aussi). Cependant par souci de cohérence utilisez des minuscules. <div onclick="function(){}">, <div onmouseover="function(){}">. Ou carrément mettre des MAJUSCULES pour justement voir tout ce qu'il reste encore à enlever du HTML.
Par pitié pas dans le href="", ce n'est pas fait pour! C'est pour une URL, pas du script!
Pas de préfixe javascript:! (si vous utilisez VB, c'est pas du web que vous faites de tt façon).
Limitez vous à une instruction dans vos évènements. onclick="var click = true;update(click);" marche probablement, mais le comportement peut être étrange. Si il y a un truc de ce genre, c'est mal coder. Il faut refaire.
Et tout ce beau monde s'execute dans l'objet global (d'où le nom) window.
window.document.getElementById("id");
window.setTimeout(fonction, 1000);
// ou
setTimeout(fonction, 1000);
document.getElementById("id");
// C'est EXACTEMENT la même chose
Puisque c'est toujours comme ça dans un navigateur, window. est sous-entendu. Ce qui nous ammène au problème suivant.
La portée
Toujours utiliservar.
C'est très facile de «coder crade» avec javascript. Une des raisons c'est que les variables déclarés à l'arrache sont «automerdiquement» des variables globales.
function mal () {
globale = "bonjour"; // cool! une variable globale!
};
alert(window.globale); // bonjour
le mot clef var permet de rendre une variable locale à la fonction parente. Cela évite donc le bordel dans le contexte global.
function bien () {
var locale = "top-secret"; // il faut savoir rester discret
};
alert(window.locale); // undefined
Beaucoup de variables globales courent le risque d'être modifiés par une partie de script et de foutre la merde dans le reste du programme.
<script><!-- --></script>
Juste… non. Les commentaires pour cacher le code javascript c'est très 90's. Si vous faites de la ratatouille de balise en faisant du pseudo XHTML ce genre de code va merder méchant. Les navigateurs digne de ce nom reconnaissent la balise script. Et au moins, si ils n'executent pas le javascript ils n'affichent pas le contenu.
Place du <script></script>
Il faut placer les balises <script> à la fin fin du HTML juste avant </body>. Cette histoire de chargement séquenciel. À la fin il ne gène pas l'affichage du html et du style, tout se passe normalement pour le visiteur donc si il y a un problème de chargement, le contenu est là. Les paillettes on s'en fout.
Les bases
Pour faire du JS il ne faut pas utiliser IE. Comme pour le CSS. On code bien et on bouche les trous après. L'idéal c'est firefox et l'extension firebug.
EVAL C'EST MAL!
Il n'y a aucune raison qu'il aparaisse dans le code. Nul part. Même pas pour transformer du JSON (utiliser une lib faite pour, en particulier json2.js. Ce script l'utilise mais c'est controlé et "futur proof" comme on dit).
Utiliser eval() démarre le compilateur javascript. C'est lent et ça bouffe de la mémoire. Rien que ça déjà… Ensuite le code est executé avec les privilèges courants, ça peut être problèmatique. Il ne peut y avoir aucune optimisation ni du code ni de la mémoire pour le script contenu dans la chaine à évaluer. Il y a toujours moyen de s'en passer. Mais ça ne s'arrete pas là…
EVAL A DES ENFANTS!
Les fonction setTimeout(), setInterval() et le constructeur Function appellent tous eval() quand on leur donne une chaine de caractère en premier paramètre et eval c'est mal! Surtout qu'il existe une méthode pour passer des paramètres à la fonction appellée par les fonctions set*
// mauvaise façon
setTimeout("mafonction("+param+","+i+")", 1000); // non!
// Bien qu'élégante cette méthode ne marche pas sous IE
// il faut utiliser la deuxième pour être compatible
setTimeout(mafonction, 1000, param, i);
// ou encore, avec une fonction anonyme, pour IE
setTimeout(function () {
mafonction(param,i);
}, 1000);
document.write();
Juste… non.
Un peu plus long: le chargement d'une page html est séquentiel. <head> chargé et parsé en premier (et il s'arrete pour tous les liens externe), 36 feuilles de style? il va chercher les 36 feuilles et télécharger 400k de css avant même d'arriver au <body>. si on rajoute à cela un <script>document.write("kikoulol);</script>. Ça devient problèmatique pour le navigateur (le js ne sera pas executé en meme temps que la page est parsée, où inserer le code écrit par javascript?). Et quand ça devient problèmatique, y'a des bugs.
if (isInternetExplorer) {}
Juste… non. Faire du code en fonction du navigateur et non pas en fonction de ce qu'il peut effectivement executer c'est suicidaire (comme un hack css en fait, c'est mal).
Il ne faut pas essayer de "deviner" le navigateur du visiteur. Il faut tester les méthodes qui existent. Je peux toujours me faire passer pour IE avec firefox, résultat la page est niqué alors qu'elle pourrait très bien marcher.
if (document.addEventListener) {
// executer le code conforme W3C
}
else if (document.attachEvent) {
// du code pour IE \o/
// meme si on triche sur le user-agent
// c'est pas grave! ça marche quand meme
}
else {
// navigateur ovni
}
On vérifie ce qu'il est disponible avant de l'utiliser. Et on ne devine pas ce qu'il est probablement possible d'utiliser basé sur le navigateur déclaré.
La notation «littérale»
Pas mal de question sur la syntaxe new Array(); vs []. Pour faire court c'est exactement pareil, sauf pour les objets y'a des feintes que l'on ne peut faire avec {} (mais à ce niveau on sait quoi).
// Petit résumé de la notation «littérale»
/**
* Les tableaux
* constructeur: Array
*/
var tab = [];// un nouveau tableau vide
var tab = ['zéro','un','deux','trois'];// avec du monde dedans
/**
* Les objets
* constructeur: Object
*/
var obj = {}; // un objet vide
var obj = { // avec du monde dedans
propriete: "valeur",
methode:function () {}
};
/**
* Les expressions régulières
* constructeur: RegExp
*/
var reg = /\d+/ig; // une regex
// je suis sur d'en oublier mais je sais plus quoi?
Question de clarté du code. Et pas de risque de se tromper avec un new qui traine là où il ne devrait pas.
La syntaxe
On a vu que ça peut devenir très «funky» le code. Seulement il faut savoir rester raisonnable et pas faire n'importe quoi.
Toujours un point virgule à la fin d'une instruction ;
C'est à dire après un appel de fonction, après une assignation de variable…
var iable = "assignation";
var icelle = appel("fonction");
// lorsque l'on assigne une fonction à une variable
var ape = function () {
/* le code de la fonction */
};
// mais attention! pas de ; lorsque l'on déclare une fonction
function attention () {
/* le code de la fonction */
}
C'est important de le faire, pour l'optimisation. Javascript essaie de rajouter par défaut (dans les 2 sens du terme d'ailleurs…) un point virgule à chaque fin de ligne si il en trouve pas.Grâce à ça, un script où il manque des points virgule marche. C'est dangereux de s'appuyer là dessus.
Un ; ça coute pas cher à écrire et tout le monde est beaucoup plus content.
Toujours mettre les deux accolades!
Pas de raison mystique, uniquement du pratique. Si on mélange les deux styles, le code n'est pas cohérent dans son ensemble, c'est donc un code pourri à maintenir. Des erreurs bêtes peuvent survenir à cause de ça…
// la mauvaise manière de faire
if (condition)
code(); // seule la ligne suivante est prise en compte!
codePasDansLaCondition();
// la manière plus propre
if (condition) {
code();
codePasDansLaCondition("hé si!");
}
Comme partout l'originalité est l'ennemi du type qui fait la maintenance. Pensez y.
Tout les autres liens que j'ai c'est en anglais. hésitez pas à en donner si vous avez des excellents liens en français.
Des conférences (géniales) en anglais : YUI theatre, c'est comme le cochon, tout est bon.
Une réference du javascript (c'est son job: «ingénieur javascript») : Douglas crowford. quirksmode.org et surtout les tables de compatibilité! Toujours avoir sous la main.
Pfiou. Je voulais écrire plus, mais c'est déjà un peu long… Posez des questions, je serai le plus clair possible. Petite remarque, les évènements c'est pas le plus important ici. Plus sur le fonctionnement du javascript en lui-même que sur le DOM en fait.
Excellent post nod_ !
Moi, qui ne suis mis que récemment au JS, il me manquait les bases (du moins mieux comprendre les modifs que je fais sur certains script JS trouvé ici et là), les bonnes habitudes à prendre, les erreurs à ne pas commettre (il y a certainement d'autres)
Merci. (post en favori)
Pour voir si j'ai bien compris certaines choses
c'est un script que j'ai posté pour aider un zéro:
<?php
$refresh = 5000; // Délai de rafraichissement (5000 = 5 secondes)
// connxeion SQL
$list = array();
$req = mysql_query("SELECT ID, nom, console FROM jeux_video WHERE console='PC'");
while($dat = mysql_fetch_assoc($req)){
$list[] = $dat['ID'].' - '.$dat['nom'].' - '.$dat['console']; // on place les info dans l'array $list
}
?>
<html>
<head>
<title>Test</title>
<script type="text/javascript">
function AffNews(nb){
var list = new Array('', '<?php echo implode("', '", $list); ?>');
var tot = list.length - 1;
if(nb == tot) nb = 1;
else nb = nb + 1;
document.getElementById('AffText').innerHTML = list[nb]+'<br /><input type="button" value=">>" onclick="AffNews('+nb+');" />';
setTimeout('AffNews('+nb+')', <?php echo $refresh; ?>);
}
</script>
</head>
<body>
<div>
<form action="" method="post">
<div id="AffText"></div>
</form>
<script type="text/javascript">AffNews(0);</script>
</div>
</body>
</html>
On va juste reprendre le bout de JS et virer le php:
function AffNews(nb){
var list = new Array('', 1, 5, 45, 62, 79);
var tot = list.length - 1;
if(nb == tot) nb = 1;
else nb = nb + 1;
document.getElementById('AffText').innerHTML = list[nb]+'<br /><input type="button" value=">>" onclick="AffNews('+nb+');" />';
setTimeout('AffNews('+nb+')', 5000);
}
Deviendrais:
function AffNews(nb){
var list = ['', 1, 5, 45, 62, 79]; /* ligne modifiée) */
var tot = list.length - 1;
if(nb == tot) nb = 1;
else nb = nb + 1;
document.getElementById('AffText').innerHTML = list[nb]+'<br /><input type="button" value=">>" onclick="AffNews('+nb+');" />';
setTimeout(AffNews, 5000, nb); /* ligne modifiée) */
}
T'as pas mis les accolades et fondamentalement c'est maladroit comme procédé. Tu recrée un tableau d'id à chaque fois que tu execute la fonction. Si il reste petit pourquoi pas, mais c'est pas tip top.
Pourquoi tu veux absolument faire commencer le tableau à 1? si c'est 0 c'est pas pour rien. Et quand tu cycle sur des élements, utiliser le % c'est vachement plus pratique quand même. J'ai changé le html vu que <button> c'est fait pour.
function AffNews(nb){
var list = [1, 5, 45, 62, 79],
nb %= list.length; // pareil que nb = nb % list.length
document.getElementById('AffText').innerHTML = list[nb] +
'<br /><button onclick="AffNews('+nb+');"><<</button>';
setTimeout(AffNews, 5000, nb);
}
J'pensais a des question plus conceptuelles mais pourquoi pas. Au moins il y aura des exemples sur lequels discuter.
Il y a d'autres trucs à éviter, comme déclarer une variable dans une boucle (faudrait tester l'impact au niveau des perfs mais par définition c'est caca).
Dans les boucle for, rappeler que la deuxième partie de la déclaration est exécutée à chaque itération. Donc si il s'agit de parcourir un array, c'est mieux de calculer le nombre d'items avant.
var array = [1, 2, 3];
for (var i=0; i<array.length; i++) {} // caca
for (var i=0, c=array.length; i<c; i++) {} // mieux
Et btw, est ce que tu n'aurais pas un lien vers des test de performance et des conseils d'optimisation pour les regex ?
Pour les performances, il y a 2–3 très bonnes videos sur yui theatre (faut gérer l'anglais par contre…). Mais ce qui est sur c'est qu'il y a des pratiques très rapides, mais très crades. Faut choisir en connaissance de cause
Les regex j'avoue que je m'en suis jamais occupé. Et je tend à penser que c'est pas au niveau du javascript qu'il faut faire ce genre de traitement. C'est déjà bien assez lent avec le DOM Ce qui me fait dire ça c'est aussi parce que je ne suis jamais tombé sur un article/présentation consacré au sujet. Et aussi parce que les regex sont optimisé par le navigateur lui même. Dans l'absolu c'est pas à toi de les optimiser.
Yop l'array il faut mettre la taille dans une variable séparée effectivement (oublie pas la condition pour autant :p). Pour le petite histoire, Mozilla implémente une fonction très pratique pour parcourir un tableau, elle prend en paramètre une fonction exécutée pour chaque élement:
Oui, forEach, JS1.6. Mais ça implique de coder la fonction pour les autres navigateurs. Autant faire une for directement .
Les regex, ça dépend effectivement fort du navigateur (IE semble d'ailleurs tous les enfoncer là dessus d'après mes derniers petits tests).
C'est aussi un truc qui m'amuse avec les différents interpréteurs JS, c'est qu'ils ne gèrent aucun les mêmes trucs de la même façons. Exemple (un peu vieux, certes)
Opera est toujours loin devant les autres pour bcp de trucs (TraceMonkey et V8 peuvent se cacher).
J'avais fait un test il y a deux semaines. Pour une auto-complétion de tags AJAX je testais quelle était la façon la plus rapide de recevoir les données et de les préparer pour les afficher (une liste de valeurs en fait), et j'avais testé 2 trucs :
- un tableau évalué (honte à moi, je sais)
- une liste de valeurs séparées par des virgules (comma separated list), à couper avec un split
Et ça me donnait ça comme résultats, en millisecondes sur une liste d'un million d'entrées (ouais, je voulais tester large ) :
Firefox 3.1 - TraceMonkey
JSON : 1205
COMMA : 257
Internet Explorer 8b2 :
JSON : 1093
COMMA : 1381
Opera 10alpha :
JSON : 453
COMMA : 203
Google Chrome 1 :
JSON : 802
COMMA : 1225
La perf de CG m'avait particulièrement déçue, pour un interpréteur JIT. Et Safari je m'en fous .
Et finalement, j'ai choisi de générer la structure des tags avec PHP et refourger le HTML à JS pour l'insérer avec un innerHTML xD .
T'es un déglingo. 1million… le json c'est l'un des rare cas ou le eval se justifie (plus pour tres longtemps, ie8 a un parseur intégré, FF devrait suivre)
T'as utilisé JSON.parse("ta chaine de ouf") pour IE8?
Non, j'avais fait un eval simple. IE8 a en effet son propre parseur sécurisé tout comme Firefox l'a depuis la version 2 mais dispo pour les extensions uniquement (à confirmer).
En fait faudrait voir si c'est vraiment utile d'utiliser un parseur JSON (en natif) à la place de eval quand on est certain de la provenance des données (qu'ils ne peut pas y avoir de code malicieux dedans). Puisque un parseur JSON natif nécessite des traitements sécurisés, peut-être que le eval est plus rapide.
J'avais essayé de faire le même test avec le DOM pour voir à quel point c'était lent (juste pour m'amuser). Comme je testais en local j'avais fait une boucle pour générer les données (insérer un million d'éléments à la suite) : j'ai jamais fini le test. IE8 a Frezzé, Firefox a crashé, Chrome je ne me souviens plus, et seul Opera a réussi Ca m'avait fait marrer (oui, on s'amuse d'un rien en faisant du JS ).
comme tu dis faut faire les tests, mais je pense que le parseur sera tout de même plus rapide qu'un eval. Mais l'interet du parseur, c'est que tu peux lui filer une fonction qui traite les données (convertir directement les dates en objets Date et co.) jamais c'est possible avec eval.
Le topic est déjà perdu dans les tréfonds du forum. Faudrait en faire un minituto, ce serait plus intéressant et mieux structuré (et la visibilité serait la même, si pas meilleure).
En tout cas merci pour ce post, ça m'a fait décider de coder un peu plus proprement le JS ...
J'ai tendance à coder un truc fonctionnel en HTML et PHP - pas nécessairement "valide" W3C, mais logique et surtout bien indenté ; c'est quand je me dis <<tiens, ce serait sympa comme tout une option "prévisualisation du message en cours" avec le BBCode >> que je ponds de ces trucs :/
Des fois je me dis que pour vous qui codez à niveau professionnel en JS, ça doit être plus qu'énervant que votre langage favori puisse être fonctionnel sans le moindre soin Courage et merci pour ce post encore une fois.
C'est cool, c'est le but du post C'est clair que le code est passablement horrible
Il faut quand même dire que c'est pas mon job (même si l'idée me plait ^^). Le js c'est un passe temps… mais je suis fan. Et les fan c'est pire.
Comme tu dis, ça me fait chier de voir du code pourri. D'autant plus que suivre ces recommendations ne limite en rien la puissance du language, au contraire.
Salut nod, et merci pour ce post qui me donne egalement envie d en finir avec mes codes degueux!
moi j ai une petite question bete: ce que tu ecris de la place du <script> ca veut pas dire que c est "mal" d'appeler un script externe dans le head? si?
je peux appeler un script avec <script type="text/javascript" src="blabla.js"></script> juste apres le </body>???
Placer le script tout à la fin te permet d'être sûr qu'il fonctionnera (et que tu ne rencontreras pas des problèmes d'appels d'éléments non chargés, etc.).
oh ouais sympa le cours. Il faut juste le mettre a jour avec les bonnes pratiques courantes, mais j'suis super étonné qu'il n'y ait rien d'ignoble sur un cours aussi vieux.
la faq js de developpez faut faire attention par contre, c'est franchement limite par endroits.
Placer le script tout à la fin te permet d'être sûr qu'il fonctionnera (et que tu ne rencontreras pas des problèmes d'appels d'éléments non chargés, etc.).
Arf, je me cite parce que c'est faux.
Tu m'as demandé si on pouvait placer le script juste après </body>.
La réponse est non... Tu le places juste avant </body>
Question qui m'a été posée : pour un onclick <button> > <a> non ? Il me semble en effet qu'un bouton est fait pour être cliqué, tandis qu'une ancre (a) mène à une autre page et basta.
le débat a/button ne tiens pas. Il faut que le site soit utilisable sans JS. Si tu fais une action en JS à laquelle correspond une url au cas ou JS soit désactivé, <a>. Sinon <button> et a plus forte raison à l'intérieur d'un formulaire.
Pour faire simple, en utilisant une fonction tu peux avoir des variables privés, et ce grâce à la magie des «closures».
Une fonction est un objet en javascript. je vais balancer du code, c'est le seul moyen d'expliquer ça rapidement.
// rien de bien méchant, un objet tout bête
var objet = {
nom: "commun",
bonjour: function () {
alert(this.nom);
}
};
// Les détails sont différents, mais au final on
// a pratiquement la même chose
var objet = new function () {
this.nom = "commun";
this.bonjour = function () {
alert(this.nom);
};
};
là tu vois rien de spécial, on utilise les deux objets de la même façon, rien de sorcier. Maintenant imagine que tu veuilles avoir une variable (ou une fonction) privée, a laquelle tu ne puisse pas accéder autrement que par des méthodes de ton objet. Tu vois bien que pour la fonction t'as un constructeur, pas pour le littéral. Qui dit fonction dit paramètres et valeurs par défaut. bingo.
var superObjet = new function () {
var secret = "abrutit"; // le var toujours, sinon c'est global
// une fonction invisible de l'extérieur de l'objet
function secrete () {
this.nom = secret;
}
this.nom = "commun";
this.bonjour = function () {
secrete(); // directement this.nom = secret; marche aussi
alert(this.nom);
}
};
Bon c'est un peu a l'arrache mais j'espère que t'as vu de quoi il s'agissait. y'a un lien en bas qui explique l'héritage et les conneries.
Pour la petite histoire j'aime pas new et j'aime bien la notation {} alors j'utilise ça en ce moment :
var objet = (function () {
var mes,variab,les,et,fonc,tions;
fonc = function () {}; // vi ça marche aussi \o/
return {
nom:"commun",
bonjour: function () {
alert(this.nom);
}
};
})(); // il faut ça pour executer la fonction et retourner l'objet.
Donc comme tu vois les objets c'est un gros gros gros morceau de JS, les closures que j'ai (mal) expliqué il faut les utiliser tout le temps, si il y a un unique truc qui déchire en javascript c'est bien ça.
D'ailleurs sans le savoir il y en a partout, dès qu'on a une fonction déclarée à l'intérieure d'une autre fonction (c'est tout a fait correct de faire ça) : pouf. Après tu as toutes les subtilités de savoir quand la closure est crée, que représente le this dans tout ce bordel (ça peut être… fun des fois
Mais la principale force de ce procédé, c'est que tu peux garder des variables en mémoire, les réutiliser sans prise de tête dans des fonctions du type setTimeout sans devoir passer des dizaines de paramètres pour connaître "l'état" de tout le bazar, il est dans les variables, et on peut y acceder facilement et sans risque qu'elles soient modifiés inopinément (bien sur si on utilise une chaine de caractère dans setTimeout on a pas tout ça… bande de nazes).
C'est long a aprivoiser, mais ô combien utile, beau et en définitive : sexy.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Ma vraie biographie - Ex-Manager de la Validation sur le Site du Zéro - sdlm.be - Horus.aero
Ma vraie biographie - Ex-Manager de la Validation sur le Site du Zéro - sdlm.be - Horus.aero
Ma vraie biographie - Ex-Manager de la Validation sur le Site du Zéro - sdlm.be - Horus.aero
Ma vraie biographie - Ex-Manager de la Validation sur le Site du Zéro - sdlm.be - Horus.aero
Ma vraie biographie - Ex-Manager de la Validation sur le Site du Zéro - sdlm.be - Horus.aero