Programmation JavaScript/Variables
Les variables permettent d'instancier un objet et de manipuler cette instance à l'aide d'un identificateur (le nom de la variable). Tous les identificateurs en JavaScript sont des références à un objet. Javascript étant un langage au typage dynamique, le type des objets n'est pas déclaré explicitement et il peut changer automatiquement.
Typage dynamique.
Il est donc possible de déclarer une variable sans lui définir de type. Ou plutôt, une variable déclarée de telle manière sera du type undefined.
Tout au long de son utilisation, cette variable pourra changer de type sans que l'interpréteur n'y trouve rien à redire.
Il n'est en fait pas nécessaire de déclarer une variable (avec le mot-clé var). La différence est expliquée ci-après.
Identificateur.
Les règles d'élaboration des identificateurs sont décrites à ce lien.
Pour résumer, les identificateurs comportent des caractères alphanumériques et le tiret soulignant (_). Ils ne peuvent commencer par un chiffre, et les majuscules comptent.
Portée.
Propriétés.
L'instanciation directe (sans mot-clé var) d'un objet avec un identificateur donne souvent l'impression de créer une variable globale. En réalité, la notion de "variable globale" n'existe pas en JavaScript. Toutes les variables sont, sauf quand on les déclare avec var, des propriétés d'un objet. Par défaut, cet objet est implicitement l'objet window (et non, window n'est pas une variable globale qui ferait exception à la règle car il est une de ses propres propriétés, c'est-à-dire que window est une propriété de window).
Comme window reste l'objet implicite la plupart du temps, une variable créée ainsi pourra être employée comme si elle était globale :
La variable mon_autre_variable est certes déclarée dans une fonction, mais il faut lire "window.mon_autre_variable" donc c'est une propriété de window et elle ne sera pas détruite automatiquement à la fin de la fonction.
Sous-entendre un objet.
Pour économiser du code, il peut être intéressant de sous-entendre un autre objet que window. Cela se fait explicitement avec un bloc with. Cette déclaration ne fonctionne pas aussi simplement qu'en Basic, par exemple. L'objet est en fait placé en haut de la "scope chain" dans le bloc d'instruction, presque comme si ses propriétés étaient des variables locales d'une fonction (voir ../Scope chain). Lorsqu'on utilise un identificateur dans un bloc with, la résolution se fait aussitôt qu'une des conditions suivantes est remplie :
Si aucune condition n'est remplie et que l'expression affecte une valeur à la variable, cette variable est créée comme propriété de l'objet global. Si l'expression ne fait qu'utiliser la valeur, elle retournera simplement undefined.
c=true
d="propriété de window"
with(monObjet)
 alert(a) // affiche la valeur de monObjet.a : "0"
 a=2 // monObjet.a existe et va être utilisé
 // si on veut utiliser window.a, il faut le faire explicitement
 window.a=5
 b="texte" // b n'est encore défini nulle part donc window.b est créé
 // si on veut créer monObjet.b, il faut le faire explicitement
 monObjet.b="autre chose"
 delete c // monObjet.c n'existe pas, mais window.c va être utilisé
 alert(d) // affiche "propriété de monObjet"
 // si on veut l'autre, il faut être explicite
 alert(window.d) // affiche "propriété de window"
Variables locales.
À l'aide de la déclaration var, il est possible de définir une variable locale, qui n'est pas une propriété d'un autre objet. Cette déclaration peut avoir lieu n'importe où dans le corps d'une fonction, pas nécessairement au début, mais seules les utilisation ultérieures de l'identificateur feront référence à la variable locale puisqu'elle n'existait pas avant.
Comme toute variable locale, elle est automatiquement détruite à la fin de la fonction, bien que sa valeur puisse être sauvegardée dans certains cas (voir Fermeture).
Par contre, elle ne peut pas être détruite par l'opérateur delete.
function foo()
 var loc_1 = 2 // n'a pas d'existence en dehors de foo()
 for (i = 0 ; i < loc_1; i++)
 var loc_2 = 2 * loc_1; // loc_2 est disponible pour toute la fonction foo() 
 alert(loc_2);
 alert(loc_2); // S'affiche sans problème
alert(loc_2); // Provoque une erreur
Si la déclaration var est utilisée en-dehors de toute fonction, la variable sera utilisable dans tout le script.
Hoisting.
L'exemple précédent montre l'effet "hositing" (levage) des déclarations avec le mot-clé var.
Les déclarations sont en effet remontées au niveau du premier bloc de code de la fonction.
Cela permet dans l'exemple précédent de continuer à accéder à la variable codice_1 après la boucle for.
Par contre, la partie initialisation n'est pas rehaussée et la variable a donc une valeur indéfinie (codice_2).
L'exemple précédent équivaut au code suivant :
function foo()
 var loc_1;
 var loc_2; // Déclaration rehaussée
 var i; // Déclaration implicite
 loc_1 = 2;
 for (i = 0 ; i < loc_1; i++)
 loc_2 = 2 * loc_1; // loc_2 est disponible pour toute la fonction foo() 
 alert(loc_2);
 alert(loc_2); // S'affiche sans problème, vaudrait undefined si loc_1 <= 0
Il en va de même pour les fonctions déclarées dans des fonctions :
function add(n)
 var t = addn(5);
 function addn(a) // ^^ déclaration rehaussée implicitement
 return a+n;
 return t;
Cela équivaut au code suivant :
function add(n)
 var t;
 function addn(a)
 return a+n;
 t = addn(5);
 return t;
Depuis Javascript 5, le mot-clé let permet de déclarer des variables locales à un bloc, sans effet de rehaussement.
function foo()
 let loc_1 = 2;
 for (i = 0 ; i < loc_1; i++)
 let loc_2 = 2 * loc_1;
 console.log(loc_2);
 console.log(loc_2); // --> Uncaught ReferenceError: loc_2 is not defined
Types.
Javascript est faiblement typé, mais typé quand même. Voici une revue des types que peut prendre une variable. Il est possible à tout moment de vérifier le type avec l'opérateur typeof.
undefined.
Une variable est du type undefined dans trois cas:
var maNouvelleVar; // Déclarée mais pas affectée
alert(typeof maNouvelleVar); // Affiche undefined
var jeSaisPas = undefined
var caNExistePas = "impossible".enFrancais
alert (typeof jeSaisPas) // Affiche undefined
alert (typeof caNExistePas) // Affiche undefined
alert(typeof varPasDeclaree); // Affiche undefined (si elle n'a effectivement pas été affectée)
monEphemere = "exister";
delete monEphemere; // la vie est courte
alert (typeof monEphemere); // Affiche undefined
alert(varPasDeclaree) // erreur !!
À noter : dans les deux premiers cas, la variable existe et possède une valeur (codice_2). On peut lire cette valeur sans provoquer d'erreur. Dans le troisième cas cependant, la variable n'existe pas : l'opérateur codice_5 retourne "undefined", mais lire directement la valeur de la variable provoque une erreur.
number.
JavaScript réunit en un seul type les entiers, petits et grands, les décimaux, et les réels, flottants ou pas.
 r = 1/3;
 alert(r); // affiche 0.333333333333
 alert(typeof r); // affiche number
 n = 4/2;
 alert(n); // affiche 2
 alert(typeof n); // affiche number
 3.6e5 // 360 000 en notation scientifique
 0x40 // 64 en notation hexadécimale
 0100 // 64 en notation octale
Concrètement, les nombres sont stockés sur 64 bits, avec une mantisse de 53 bits. Cela permet des valeurs entières jusqu'à 9 007 199 254 740 991, à partir duquel on commence à perdre de la précision jusqu'à 2^1024-1 qui est la valeur flottante la plus élevée représentable en JavaScript.
En fait, JavaScript inclut également dans le type number deux valeurs spéciales. Infinity, un nombre signé, est renvoyé dès que le résultat dépasse la capacité de stockage de number, mais aussi dans des cas limites simples. Dans les cas indécidables, ou bien lorsqu'une opération qui retourne un nombre ne peut pas le faire, c'est la valeur NaN (Not A Number) qui est renvoyée (voir les opérateurs arithmétiques, les méthodes de Number et les méthodes de Math pour les cas qui produisent ces deux valeurs).
1/0 == Infinity
-2 / Infinity == 0
0/0 == NaN
Number("texte") == NaN
string.
Le type string stocke une chaîne de caractères. C'est aussi le type le plus "faible" du langage : n'importe quelle valeur peut être convertie en chaîne, et en cas de doute avec l'opérateur +, c'est une concaténation qui aura lieu, pas une addition.
Tous les objets possèdent une méthode générique toString() "magique" qui est appelée lorsqu'il faut convertir leur valeur en chaîne. Elle peut bien sûr être écrasée par une méthode plus spécifique.
Les chaînes de caractères en JavaScript sont encadrées par des guillemets droits ("double quotes", ") ou des "single quotes" ('), au choix. Il n'y a pas de distinction entre des chaînes définies avec l'un ou l'autre, mais si on commence une chaîne par un type de guillemet il faut la finir par le même type. À l'intérieur d'une chaîne, les séquences d'échappement connues fonctionnent comme partout :
etc.
Enfin, JavaScript utilise des chaînes de caractères Unicode multi-octets. Quel que soit l'encodage du fichier où est écrit le script, il est toujours possible d'inclure n'importe quel caractère dans une chaîne, soit en récupérant une chaîne d'un autre fichier, soit en créant une chaîne avec la méthode statique String.fromCharCode(), soit en utilisant la séquence d'échappement :
boolean.
Une variable de type boolean accepte deux valeurs : vrai et faux.
b = true;
alert(typeof b); // Affiche "boolean"
c = (5 == 3); // est faux, mais l'expression est booléenne.
alert(typeof c); // Affiche "boolean"
Dans une comparaison, les valeurs suivantes sont équivalentes (opérateur ==) à false :
Par ailleurs, les valeurs suivantes donnent false si on les convertit (explicitement ou pas) en booléen, bien qu'elle ne soient pas toutes équivalentes par comparaison :
Comme ces listes le montrent, une valeur peut être équivalente à false sans être considérée comme fausse elle-même ! Et inversement, une valeur considérée comme fausse n'est pas forcément équivalente au booléen false.
Cela peut sembler surprenant et illogique à première vue (et source de quelques bugs) mais il faut comprendre l'origine de ces différences :
Une valeur qui n'est pas équivalente à false ne sera pas pour autant équivalente à true par comparaison, à commencer par null, NaN et undefined mais aussi toute valeur qui n'est pas équivalente à l'une des conversions possibles de true (la chaîne "true", la chaîne ou le nombre 1).
Par contre, toutes les valeurs qui ne donnent pas false par conversion en boolean donnent true, sans exception.
Attention, les valeurs de type object, y compris les instances des classes String, Number et Boolean créées avec le constructeur new sont toujours converties en true, même lorsque leur valeur est "", 0 ou false (la seule exception est null qui est de type object et dont la conversion en booléen est false). Cependant la comparaison avec false fonctionne de la même façon qu'avec des valeurs littérales.
object.
Une variable de type object assume pleinement sa nature. Les autres types de variables, dits "scalaires", se comportent comme s'ils étaient eux-mêmes une valeur. Ainsi, lorsqu'on passe une variable scalaire à une expression, sa valeur est clonée. En revanche, passer une variable de type object ne fait que copier la référence, et la valeur n'est pas clonée.
L'existence de "classes" en JavaScript est discutée. Le langage ne possède pas de déclaration de classe en tant que telle. Pour cette raison, l'utilisation de ce terme sera mise entre guillemets.
Il existe trois manières de créer une variable de type object.
L'instanciation explicite d'un objet grâce à un constructeur s'effectue avec l'opérateur new.
t = new Array();
alert(typeof t) // Affiche "object"
Les expressions littérales de chaînes, nombres et booléens créent des variables scalaires alors que le constructeur de ces types crée un objet (explicitement de type object et donc strictement différents d'une valeur scalaire équivalente). Mais les expressions littérales de fonctions, tableaux et expressions rationnelles créent des objets, car ce ne sont pas des types scalaires. Ces objets, instanciés sans utilisation de new, sont néanmoins strictement identiques à leur équivalent produit par l'appel d'un constructeur. Par exemple :
// valeur scalaire vs. objet
"chaîne" !== new String("chaîne")
2.34 !== new Number(2.34)
// objet vs. objet... c'est pareil !
["a", "b", "c"] === new Array("a", "b", "c")
/(\w+) ?\(/i === new RegExp("(\w+) ?\(", "i")
On peut créer un objet générique (qui sera simplement une instance de la "classe" Object) avec la notation littérale. Cela consiste à écrire, entre accolades, une liste de couples clé : valeur séparés par des virgules, la clé pouvant être un identifiant ou bien une chaîne, et la valeur pouvant être n'importe quelle valeur. La clé et la valeur sont séparés par deux points (':').
objetLitteral = {
 nom : "Mac OS X",
 version : 10.5,
 "nom de code" : "Leopard"
Attention, dans l'exemple ci-dessus, on ne pourra accéder à la dernière propriété ("nom de code") de l'objet qu'avec la notation tableau (avec des crochets), pas la notation objet (avec des points) car ce n'est pas un identifiant valide (il y a des espaces dedans).
Le DOM (aussi bien W3C que IE) définit certaines "classes" : HTMLElement, HTMLTextNode, HTMLDocument, CSSRuleSet, CSSRule, etc. On doit utiliser les méthodes du DOM (comme document.createElement()) pour instancier ces objets (le "DOM 0" fait exception à cette règle et à bien d'autres).
Les autres "classes" d'objets ne peuvent être créés que par un constructeur. Il y a des constructeurs natifs tels que Date, et on peut définir un constructeur soi-même. Pour plus de détails, voir le chapitre consacré aux objets.
function.
La différence essentielle entre une variable de type function et une variable de type object, est qu'on peut appeler une fonction. Dans les deux sens du terme :
Attention : ne pas confondre le type de variable ("object", "function") avec les constructeurs homonymes (Object, Function). Une variable de type "object" peut avoir n'importe quel constructeur sauf Function et bien des comportements différents, tous différents du comportement d'une fonction. Mais cette section est uniquement consacrée au type "function".
Contrairement aux autres types, le type "function" n'a pas de valeur spéciale (telle que NaN pour "number" ou null pour "object"). Il n'y a aucun moyen de convertir directement un autre type en fonction.
Une fonction est créée par l'expression littérale (exemple ci-dessous) ou bien par le constructeur Function.
function fibonacci( n )
 if(isNaN(n) || n<0)
 return undefined
 n=Math.floor(n)
 var u = 1, v = 0
 while(n--)
 u = v + (v = u)
 return v
Cette déclaration crée une variable "fibonacci" qui référence la fonction, mais c'est aussi une opération qui retourne la référence de la fonction, que l'on peut ainsi affecter à une autre variable. Il est d'ailleurs possible de déclarer une fonction anonyme. De plus, toute nouvelle fonction reçoit automatiquement une propriété prototype identique au prototype de la fonction Object.
L'appel se fait en ouvrant et fermant une paire de parenthèses juste après une expression dont la valeur est de type "function". Cette expression n'est pas obligatoirement le nom d'une fonction, ni même un identifiant. Cette signification des parenthèses est prioritaire sur la signification générale (forcer une expression à être évaluée avant celles qui l'entourent).
f = fibonacci
delete fibonacci // efface seulement la variable, pas la fonction, qui est toujours référencée par f
x = f(10); // Constitue l'appel de la fonction fibonacci, en lui passant un argument
alert(f) // affiche le code de la fonction, y compris le nom donné dans sa déclaration, "fibonacci"
Les opérateurs et méthodes qui acceptent des objets comme opérande ou argument fonctionnent pareil avec des fonctions, notamment le point pour accéder aux propriétés de la fonction.
L'opérateur new n'accepte que des fonctions comme opérande.
Par défaut, la conversion d'une fonction en chaîne retourne le code source de la fonction, "préalablement reformaté par l'interpréteur JavaScript" : les point-virgules, sauts de ligne et indentations seront normalisés.