Chapitre 217 minDébutant
Variables, types et opérateurs : let, const et ===
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- déclarer une variable avec
constoulet, et dire pourquoi évitervar; - reconnaître les types de données et les vérifier avec
typeof; - convertir une saisie en nombre sans piège ;
- comparer avec
===et prévoir le résultat d'une condition.
4 exercices corrigés
Une variable JavaScript est un nom qui désigne une valeur ; on la déclare avec const quand elle ne sera pas réaffectée, avec let sinon. Ce chapitre présente les déclarations, les types de données du langage, les conversions entre types, et les opérateurs, avec les pièges qui surprennent tous ceux qui viennent d'un autre langage. Les notions générales (variable, affectation, type) sont expliquées dans le cours Algorithmique : ici, on regarde comment JavaScript les applique.
Déclarer une variable : const, let, et pourquoi oublier var
const TAUX_TVA = 0.2; // ne changera pas : const
let quantite = 12; // va changer : let
quantite = quantite - 1; // réaffectation autorisée avec let
console.log(quantite); // 11
La règle du code moderne : const par défaut, let quand la variable doit être réaffectée, jamais var. Une variable déclarée avec const ne peut pas recevoir une autre valeur : si tu essaies, le navigateur s'arrête sur TypeError: Assignment to constant variable. C'est une protection, pas une contrainte : en lisant const, tu sais que la valeur ne bougera plus.
var est l'ancienne façon de déclarer, d'avant 2015. Elle a deux défauts qui causent des bugs difficiles à trouver :
console.log(stock); // undefined : pas d'erreur, alors que stock n'est pas encore déclaré
var stock = 5;
if (true) {
var message = 'visible partout';
}
console.log(message); // 'visible partout' : var ignore les blocs { }
Avec let ou const, lire une variable avant sa ligne de déclaration provoque une erreur claire (ReferenceError: Cannot access 'stock' before initialization), et une variable déclarée dans un bloc { } n'existe que dans ce bloc. Tu croiseras var dans du code ancien ; ne l'écris pas.
var | let | const | |
|---|---|---|---|
| Portée | La fonction entière | Le bloc { } | Le bloc { } |
| Réaffectation | Oui | Oui | Non |
| Redéclaration du même nom | Oui, sans erreur | Non (SyntaxError) | Non (SyntaxError) |
| Lecture avant la déclaration | undefined | ReferenceError | ReferenceError |
| À utiliser | Jamais | Compteurs, totaux, valeurs qui évoluent | Tout le reste |
Nommer les variables
Un nom commence par une lettre, _ ou $, puis contient des lettres, des chiffres, _ ou $. JavaScript distingue majuscules et minuscules : quantite et Quantite sont deux variables différentes. Les conventions du langage :
- camelCase pour les variables et les fonctions :
prixUnitaire,seuilAlerte,valeurStock; - PascalCase pour les classes :
Piece; - MAJUSCULES_SÉPARÉES pour les constantes de configuration connues à l'avance :
TAUX_TVA,URL_API.
Choisis des noms qui disent ce que contient la variable : quantite plutôt que q, piecesEnAlerte plutôt que tab2. Les accents sont autorisés dans les noms mais déconseillés : les claviers de tes collègues et certains outils les gèrent mal. Les mots réservés du langage (let, class, function, return, new, delete…) ne peuvent pas servir de nom.
Les types de données en JavaScript
JavaScript est typé dynamiquement : tu ne déclares pas le type d'une variable, c'est la valeur qui a un type, et une même variable peut recevoir une valeur d'un autre type. Le langage connaît huit types : sept types primitifs et les objets.
| Type | Exemples | typeof renvoie | À savoir |
|---|---|---|---|
number | 12, 42.9, -3, NaN, Infinity | 'number' | Un seul type pour les entiers et les décimaux |
string | 'SSD-512', "Écran", `${q} pièces` | 'string' | Immuable : on ne modifie pas un caractère |
boolean | true, false | 'boolean' | |
undefined | undefined | 'undefined' | Variable déclarée sans valeur, propriété absente |
null | null | 'object' | « Pas de valeur », écrit volontairement ; typeof se trompe, c'est une erreur historique du langage |
bigint | 9007199254740993n | 'bigint' | Entiers de taille illimitée, rares en web |
symbol | Symbol('id') | 'symbol' | Identifiants uniques, usage avancé |
object | { ref: 'SSD-512' }, [1, 2], new Date() | 'object' ('function' pour une fonction) | Tout ce qui n'est pas primitif, tableaux et fonctions compris |
typeof 42 // 'number'
typeof 'Écran' // 'string'
typeof undefined // 'undefined'
typeof null // 'object' (piège)
typeof [] // 'object' (un tableau est un objet)
typeof (() => 1) // 'function'
Array.isArray([]) // true (la bonne façon de tester un tableau)
Pour tester null, écris valeur === null, jamais typeof valeur === 'null' (toujours faux).
Les nombres : décimaux, arrondis et NaN
Tous les nombres sont des nombres à virgule flottante sur 64 bits (norme IEEE 754), comme le double de C#. Conséquence : certains décimaux ne sont pas représentés exactement.
0.1 + 0.2 // 0.30000000000000004
1.1 + 2.2 // 3.3000000000000003
(2.345).toFixed(2) // '2.35' : une chaîne, pas un nombre
(1.005).toFixed(2) // '1.00' : 1.005 est stocké un peu en dessous de 1,005
Math.round(2.5) // 3
Math.round(-2.5) // -2
7 % 3 // 1 (reste de la division entière)
2 ** 10 // 1024 (puissance)
10 / 0 // Infinity : pas d'erreur
0 / 0 // NaN
NaN (Not a Number) est le résultat d'un calcul impossible, par exemple Number('douze'). Il a deux propriétés déroutantes : il est de type number, et il n'est égal à rien, pas même à lui-même (NaN === NaN vaut false). Teste-le avec Number.isNaN(valeur). Un NaN se propage : NaN + 1 vaut NaN, et tout calcul qui en dépend aussi.
Les chaînes de caractères et les gabarits
Une chaîne s'écrit entre apostrophes '…', guillemets "…" ou accents graves `…`. Les accents graves créent un gabarit (template literal) : tu y insères une expression avec ${…}, et tu peux aller à la ligne.
const ref = 'RAM-16';
const quantite = 3;
console.log(`${ref} : ${quantite} pièce${quantite > 1 ? 's' : ''} en stock`);
// RAM-16 : 3 pièces en stock
Les méthodes de chaîne que tu utiliseras le plus :
| Écriture | Résultat | Rôle |
|---|---|---|
'SSD NVMe 512 Go'.length | 15 | Nombre de caractères |
' ssd-512 '.trim().toUpperCase() | 'SSD-512' | Retirer les espaces autour, passer en majuscules |
'SSD-512'.includes('512') | true | Contient ? |
'ram-16'.startsWith('RAM') | false | Commence par ? (sensible à la casse) |
'SSD-512'.slice(0, 3) | 'SSD' | Extraire (début inclus, fin exclue) |
'SSD-512'.slice(-3) | '512' | Les 3 derniers caractères |
'SSD-512'.split('-') | ['SSD', '512'] | Découper en tableau |
String(7).padStart(3, '0') | '007' | Compléter à gauche |
'Dalle 15,6 pouces'.replace(',', '.') | 'Dalle 15.6 pouces' | Remplacer la première occurrence (replaceAll pour toutes) |
'Réparinfo'.at(-1) | 'o' | Caractère à une position, négative depuis la fin |
Une chaîne est immuable : s[0] = 'X' ne fait rien. Les méthodes renvoient toujours une nouvelle chaîne, qu'il faut ranger dans une variable.
Les conversions de type
Une saisie de formulaire est toujours une chaîne, même dans un champ type="number". Avant de calculer, convertis-la explicitement.
Number('12') // 12
Number(' 42 ') // 42 (espaces ignorés)
Number('') // 0 (attention : un champ vide devient 0)
Number('12abc') // NaN
Number('3,5') // NaN (la virgule française n'est pas comprise)
parseInt('12abc', 10) // 12 (lit le début, s'arrête au premier caractère invalide)
parseFloat('3.5 kg') // 3.5
String(12) // '12'
Number est strict : la chaîne entière doit être un nombre. parseInt et parseFloat sont tolérants et lisent ce qu'ils peuvent : pratique pour '3.5 kg', dangereux pour une saisie qu'on veut contrôler. Passe toujours la base 10 à parseInt. Pour accepter une virgule décimale, remplace-la avant : Number(saisie.replace(',', '.')).
JavaScript convertit aussi tout seul quand tu mélanges les types, et c'est la source de bugs classiques :
| Expression | Résultat | Pourquoi |
|---|---|---|
'5' + 3 | '53' | + avec une chaîne concatène |
'5' - 3 | 2 | - n'existe que pour les nombres : la chaîne est convertie |
'5' * '2' | 10 | idem pour * |
true + 1 | 2 | true devient 1 |
null + 1 | 1 | null devient 0 |
undefined + 1 | NaN | undefined devient NaN |
'1' + 12 + 1 | '1121' | De gauche à droite : '1' + 12 donne '112' |
12 + 1 + '1' | '131' | 12 + 1 donne 13, puis concaténation |
Comparer : == ou === ?
=== (égalité stricte) compare la valeur et le type, sans conversion. == (égalité large) convertit d'abord, avec des règles si tordues que presque personne ne les connaît par cœur :
0 == '' // true
0 == '0' // true
'' == '0' // false (pourtant les deux sont égaux à 0…)
null == undefined // true
null === undefined // false
3 === '3' // false
Utilise toujours === et !==. Les outils d'analyse de code comme ESLint ont une règle pour l'imposer à toute une équipe (eqeqeq).
Les comparaisons <, >, <=, >= entre deux chaînes comparent l'ordre des caractères, pas les nombres : '10' < '9' vaut true, parce que le caractère '1' vient avant '9'. Encore une raison de convertir les saisies.
Valeurs « truthy » et « falsy »
Dans un if, n'importe quelle valeur est convertie en booléen. Seules huit valeurs deviennent false : on les appelle falsy.
| Valeurs falsy | Tout le reste est truthy, y compris |
|---|---|
false, 0, -0, 0n, '', null, undefined, NaN | '0', 'false', ' ', [], {}, -1 |
const nom = '';
if (nom) {
console.log('nom saisi');
} else {
console.log('nom vide'); // affiché : '' est falsy
}
C'est pratique pour tester une chaîne vide, mais piégeux avec les nombres : une quantité de 0 est falsy, alors que c'est une vraie quantité.
Les opérateurs
| Famille | Opérateurs | Exemple |
|---|---|---|
| Arithmétiques | + - * / % ** | quantite * prix |
| Affectation | = += -= *= /= ??= | total += prix |
| Incrément | ++ -- | i++ |
| Comparaison | === !== < > <= >= | quantite <= seuil |
| Logiques | && || ! | quantite > 0 && !enAlerte |
| Coalescence des nuls | ?? | fournisseur ?? 'inconnu' |
| Ternaire | condition ? a : b | quantite > 1 ? 's' : '' |
&& et || ne renvoient pas forcément un booléen : ils renvoient l'une des deux valeurs. a || b renvoie a s'il est truthy, sinon b. a ?? b renvoie a sauf s'il vaut null ou undefined. La différence compte dès que 0 ou '' sont des valeurs valables :
const quantiteSaisie = 0;
quantiteSaisie || 10 // 10 : 0 est falsy, la valeur par défaut l'écrase (bug)
quantiteSaisie ?? 10 // 0 : ?? ne remplace que null et undefined
'' || 'sans nom' // 'sans nom'
'' ?? 'sans nom' // ''
i++ renvoie la valeur avant l'incrément, ++i la valeur après : avec let i = 5, console.log(i++, i, ++i) affiche 5 6 7. Pour éviter toute confusion, écris l'incrément sur sa propre ligne.
Fil rouge : une pièce en variables
Avant de ranger les pièces dans un objet, décris-en une avec des variables et calcule sa valeur en stock :
const ref = 'RAM-16';
const nom = 'Barrette DDR4 16 Go';
let quantite = 3;
const seuil = 4;
const prix = 38.5;
quantite += 2; // réception de 2 barrettes
const valeur = quantite * prix;
const enAlerte = quantite <= seuil;
console.log(`${ref} (${nom}) : ${quantite} en stock, valeur ${valeur.toFixed(2)} €`);
console.log(`Alerte : ${enAlerte}`);
RAM-16 (Barrette DDR4 16 Go) : 5 en stock, valeur 192.50 €
Alerte : false
toFixed(2) donne un point décimal : l'affichage français propre, avec virgule et espace, viendra avec Intl au chapitre 6.
Exercices
Exercice 1 sur 4 Prédire les types et les valeurs Réussi À revoir
Sans exécuter le code, donne le résultat de chaque expression, puis vérifie dans la console.
typeof '12''12' + 3'12' - 3Number('')Number('12,5')typeof null0 ?? 50 || 5'10' < '9'NaN === NaN
Voir la réponseMasquer la réponse de l'exercice 1
'string': les guillemets en font une chaîne.'123':+avec une chaîne concatène.9:-force la conversion en nombre.0: la chaîne vide devient 0, un piège pour les champs vides.NaN:Numberne comprend pas la virgule.'object': erreur historique du langage.0:??ne remplace quenulletundefined.5:0est falsy.true: comparaison de chaînes caractère par caractère,'1'vient avant'9'.false:NaNn'est égal à rien ; teste-le avecNumber.isNaN.
Tu avais trouvé ?
Exercice 2 sur 4 Corriger le calcul du panier Réussi À revoir
Ce code doit afficher le total d'une commande de pièces : 2 chargeurs à 24 €, sans frais de port. Il affiche Total : 480 €. Trouve la cause, puis corrige le code en appliquant les bonnes pratiques du chapitre.
var quantite = champQuantite.value; // '2'
var prix = 24;
var fraisPort = '0';
var total = quantite * prix + fraisPort;
console.log('Total : ' + total + ' €');
Voir la réponseMasquer la réponse de l'exercice 2
quantite * prix donne bien 48, car * convertit la chaîne '2' en nombre. Mais fraisPort est une chaîne : 48 + '0' concatène et donne '480'. Le calcul ne marchait que par chance pour la multiplication.
Corrections : convertir chaque saisie en nombre dès sa lecture, pour ne jamais dépendre des conversions automatiques, et remplacer var par const.
const quantite = Number(champQuantite.value);
const prix = 24;
const fraisPort = Number('0');
const total = quantite * prix + fraisPort;
console.log(`Total : ${total} €`); // Total : 48 €
Tu avais trouvé ?
Exercice 3 sur 4 Valeur par défaut Réussi À revoir
Une pièce peut avoir un champ remise (en pourcentage) qui vaut 0, 10, ou qui est absent (undefined). On écrit const taux = remise || 5; pour appliquer 5 % par défaut. Quelle valeur prend taux pour chacun des trois cas ? Quelle écriture faut-il utiliser ?
Voir la réponseMasquer la réponse de l'exercice 3
Avec || : remise = 0 donne 5 (bug : une remise nulle volontaire est remplacée), remise = 10 donne 10, remise = undefined donne 5.
Il faut const taux = remise ?? 5; : 0 donne 0, 10 donne 10, undefined donne 5.
Tu avais trouvé ?
Exercice 4 sur 4 Statut d'une pièce Réussi À revoir
Avec les variables quantite et seuil d'une pièce, écris les expressions booléennes enRupture (plus aucune unité), enAlerte (quantité inférieure ou égale au seuil) et aCommander (en alerte mais pas en rupture). Donne leur valeur pour CHG-65 (quantité 0, seuil 2) et pour ECR-156 (quantité 2, seuil 1).
Voir la réponseMasquer la réponse de l'exercice 4
const enRupture = quantite === 0;
const enAlerte = quantite <= seuil;
const aCommander = enAlerte && !enRupture;
- CHG-65 :
enRupturevauttrue,enAlertevauttrue(0 ≤ 2),aCommandervautfalse. - ECR-156 :
enRupturevautfalse,enAlertevautfalse(2 > 1),aCommandervautfalse.
Tu avais trouvé ?