Cours JavaScript et TypeScript · Module 2: Fonctions, tableaux et objets

Template literals et méthodes de chaînes

8 min de lecture
Dans cette leçon8

Au laboratoire de Jurassic Park, des rapports sont produits toutes les heures : espèce, poids, zone, état de santé. Les assembler avec des plus se transforme vite en forêt de guillemets, et les données des capteurs arrivent avec des espaces en trop et une casse aléatoire. Les template literals et les méthodes de chaînes sont les outils qui mettent de l'ordre dans le texte : de l'insertion de variables à la recherche et à la transformation de chaînes de caractères.

Template literals - les bases

Les template literals sont des chaînes entourées d'accents graves, c'est-à-dire d'apostrophes inversées, et non de guillemets ordinaires. Tu y insères une expression JavaScript avec la syntaxe ${...} :

1const species = 'T-Rex';
2const weight = 8000;
3
4// L'ancienne méthode - la concaténation
5const old = 'Espèce : ' + species + ', poids : ' + weight + ' kg';
6
7// Template literal - bien plus lisible !
8const report = `Espèce : ${species}, poids : ${weight} kg`;
9console.log(report); // "Espèce : T-Rex, poids : 8000 kg"

Les deux variables contiennent le même texte, mais dans le gabarit on voit tout de suite la forme de la phrase. C'est pourquoi je te recommande les template literals dès qu'une seule variable apparaît dans le texte.

Les expressions à l'intérieur de ${}

À l'intérieur de ${}, on peut placer n'importe quelle expression JavaScript, pas seulement une variable. L'opérateur conditionnel, l'arithmétique, l'appel d'une méthode et même un template literal imbriqué fonctionnent ici :

1const dino = { name: 'Rex', health: 85 };
2
3console.log(`Statut : ${dino.health > 50 ? 'stable' : 'critique'}`);
4// "Statut : stable"
5
6console.log(`Santé : ${dino.health}%`);
7// "Santé : 85%"
8
9console.log(`Double dose : ${dino.health * 2} unités`);
10// "Double dose : 170 unités"
11
12console.log(`Nom : ${dino.name.toUpperCase()}`);
13// "Nom : REX"

JavaScript calcule d'abord l'expression, convertit ensuite le résultat en texte et le colle à la place de ${}. L'objet dino reste intact pendant ce temps.

Les chaînes multilignes

Les template literals conservent la mise en forme sur plusieurs lignes, pas besoin donc d'insérer \n :

1const dinoCard = `
2╔════════════════════════╗
3║  Espèce : T-Rex        ║
4║  Zone : A              ║
5║  Statut : Actif        ║
6╚════════════════════════╝
7`;
8console.log(dinoCard);

Chaque retour à la ligne et chaque espace à l'intérieur des accents graves se retrouvent dans le résultat, y compris la ligne vide du début.

Les tagged templates

Les tagged templates permettent de traiter un gabarit avec une fonction. La fonction "de tag" reçoit un tableau des fragments de texte fixes ainsi que les valeurs des expressions, et il y a toujours un fragment de plus que de valeurs :

1function highlight(strings, ...values) {
2  let result = '';
3  strings.forEach((str, i) => {
4    result += str;
5    if (i < values.length) {
6      result += '**' + values[i] + '**';
7    }
8  });
9  return result;
10}
11
12const name = 'Rex';
13const zone = 'A';
14const tagged = highlight`Dinosaure ${name} dans la zone ${zone}`;
15console.log(tagged);
16// "Dinosaure **Rex** dans la zone **A**"

Remarque qu'on appelle le tag sans parenthèses : le nom de la fonction se trouve juste devant l'accent grave. Un usage pratique est la construction de requêtes vers une base de données, où les valeurs sont transmises séparément du texte de la requête :

1function sql(strings, ...values) {
2  const query = strings.join('?');
3  return { query, params: values };
4}
5
6const zoneId = 'A';
7const minHealth = 80;
8const query = sql`SELECT * FROM dinos WHERE zone = ${zoneId} AND health > ${minHealth}`;
9// { query: "SELECT * FROM dinos WHERE zone = ? AND health > ?", params: ['A', 80] }

Les valeurs n'ont pas été collées dans le texte de la requête, elles ont été mises de côté dans params. C'est sur ce principe que reposent les requêtes paramétrées, qui protègent la base contre les attaques par injection SQL.

Méthodes de chaînes - la recherche

Commençons par chercher des fragments dans une séquence d'ADN. Chacune de ces méthodes se contente de lire le texte et de renvoyer une réponse :

1const dnaSequence = 'ATCG-TTGA-ACGT-TTGA';
2
3// indexOf() - renvoie l'indice de la première occurrence (ou -1)
4console.log(dnaSequence.indexOf('TTGA'));    // 5
5console.log(dnaSequence.indexOf('XXXX'));    // -1
6console.log(dnaSequence.lastIndexOf('TTGA')); // 15 - la dernière occurrence
7
8// includes() - vérifie si la chaîne contient un fragment (boolean)
9console.log(dnaSequence.includes('ACGT'));   // true
10console.log(dnaSequence.includes('ZZZZ'));   // false
11
12// startsWith() / endsWith() - vérifie le début/la fin
13console.log(dnaSequence.startsWith('ATCG')); // true
14console.log(dnaSequence.endsWith('TTGA'));   // true
15console.log(dnaSequence.startsWith('TTGA', 5)); // true - à partir de la position 5
16
17// match() avec une expression régulière - toutes les correspondances
18console.log(dnaSequence.match(/TTGA/g)); // ['TTGA', 'TTGA']

Toutes ces méthodes sont sensibles à la casse, donc includes('acgt') renverra false. Quand tu demandes seulement "est-ce que c'est là ?", choisis includes(), qui se lit comme une phrase, et garde indexOf() pour les cas où tu as besoin de la position.

Méthodes de chaînes - découper et diviser

Le découpage est utile quand, dans le nom complet d'un spécimen, tu n'as besoin que d'une partie :

1const specimen = 'Tyrannosaurus Rex';
2
3// slice(start, end) - extrait un fragment de la chaîne
4console.log(specimen.slice(0, 13));    // "Tyrannosaurus"
5console.log(specimen.slice(14));       // "Rex"
6console.log(specimen.slice(-3));       // "Rex" (depuis la fin)
7
8// split() - divise la chaîne en tableau
9const zones = 'A-B-C-D'.split('-');
10console.log(zones); // ['A', 'B', 'C', 'D']
11
12const words = 'Jurassic Park est génial'.split(' ');
13console.log(words); // ['Jurassic', 'Park', 'est', 'génial']

slice n'inclut pas le caractère d'indice end, et un indice négatif compte depuis la fin, donc slice(-3) donne les trois derniers caractères. split renvoie un tableau, que tu décomposeras dans la prochaine leçon grâce à la déstructuration.

Méthodes de chaînes - les transformations

Les données brutes des capteurs doivent être nettoyées. Règle essentielle : les chaînes sont immuables, donc chaque méthode renvoie une nouvelle chaîne :

1const dinoName = '  velociraptor  ';
2
3// trim() - supprime les espaces blancs au début et à la fin
4console.log(dinoName.trim());      // "velociraptor"
5console.log(dinoName.trimStart()); // "velociraptor  "
6console.log(dinoName.trimEnd());   // "  velociraptor"
7
8// toUpperCase() / toLowerCase()
9console.log('rex'.toUpperCase());         // "REX"
10console.log('T-REX'.toLowerCase());       // "t-rex"
11
12// replace() - remplace la première occurrence
13const text = 'Zone A, Zone B, Zone A';
14console.log(text.replace('Zone', 'Secteur'));  // "Secteur A, Zone B, Zone A"
15
16// replaceAll() - remplace toutes les occurrences
17console.log(text.replaceAll('Zone', 'Secteur')); // "Secteur A, Secteur B, Secteur A"

Après ces appels, dinoName a toujours ses espaces, et text contient toujours "Zone". Enregistre le résultat dans une nouvelle variable si tu veux le garder. La différence entre replace et replaceAll porte donc sur le nombre de remplacements, pas sur la modification de l'original.

Méthodes de chaînes - le remplissage

Les méthodes padStart() et padEnd() complètent une chaîne jusqu'à une longueur donnée, et repeat() la répète. Ensemble, elles permettent d'aligner un tableau de spécimens :

1// padStart(length, fillString) - complète par le début
2const dinoId = '42';
3console.log(dinoId.padStart(5, '0'));  // "00042"
4console.log(dinoId.padStart(5, '-'));  // "---42"
5
6// padEnd(length, fillString) - complète par la fin
7const label = 'Rex';
8console.log(label.padEnd(10, '.'));    // "Rex......."
9
10// repeat(count) - répète la chaîne
11console.log('-'.repeat(30));           // "------------------------------"
12
13// Un exemple pratique - la mise en forme d'un tableau
14const specimens = [
15  { id: '1', name: 'T-Rex' },
16  { id: '42', name: 'Velociraptor' },
17  { id: '100', name: 'Triceratops' }
18];
19
20specimens.forEach(s => {
21  const formattedId = s.id.padStart(4, '0');
22  const formattedName = s.name.padEnd(15, ' ');
23  console.log(`ID: ${formattedId} | Espèce : ${formattedName}`);
24});
25// ID: 0001 | Espèce : T-Rex
26// ID: 0042 | Espèce : Velociraptor
27// ID: 0100 | Espèce : Triceratops

Le premier argument est la longueur visée pour toute la chaîne, et non le nombre de caractères à ajouter. C'est pourquoi '42'.padStart(5, '0') ajoute trois zéros.

Enchaîner les méthodes (chaining)

On peut enchaîner les méthodes de chaînes, car chacune renvoie une nouvelle chaîne sur laquelle tu appelles la suivante :

1const messy = '  jurassic PARK  est  GÉNIAL  ';
2const clean = messy.trim().toLowerCase().replace(/\s+/g, ' ');
3console.log(clean); // "jurassic park est génial"
4
5const dinoCode = 'trex-001';
6const formatted = dinoCode.split('-')
7  .map(part => part.toUpperCase())
8  .join('-');
9console.log(formatted); // "TREX-001"

/\s+/g est une expression régulière qui désigne n'importe quelle suite d'espaces blancs, et le drapeau g demande de remplacer toutes les occurrences. Le même schéma produit des adresses de rapports lisibles :

1function formatReport(rawInput) {
2  return rawInput
3    .trim()
4    .toLowerCase()
5    .replace(/\s+/g, '-')       // espaces en tirets
6    .replace(/[^a-z0-9-]/g, '') // supprime tout sauf les lettres, chiffres et tirets
7    .slice(0, 50);             // 50 caractères maximum
8}
9
10console.log(formatReport('  T-Rex Escape!! Zone A  '));
11// "t-rex-escape-zone-a"

Chaque étape de la chaîne fait une seule chose, elle est donc facile à lire et à corriger. Je te recommande de couper les longues chaînes comme ici, une méthode par ligne.

Pratique

Dans le bac à sable t'attendent des gabarits de rapports, la recherche dans l'ADN, slice, split, replace et le remplissage. Change les arguments et prédis le résultat avant d'exécuter le code.

Code de cette leçon : index.js
1// === TEMPLATE LITERALS ET MÉTHODES DES STRINGS ===
2
3// 1. Template literals - insertion d'expressions
4const species = 'T-Rex';
5const weight = 8000;
6
7console.log('=== Template Literals ===');
8console.log(`Espèce: ${species}, poids: ${weight} kg`);
9console.log(`Santé: ${weight > 5000 ? 'grand spécimen' : 'petit spécimen'}`);
10console.log(`Double dose: ${weight * 0.05} kg`);
11
12// 2. Chaînes multilignes
13const card = `
14=== CARTE DU DINOSAURE ===
15Espèce: ${species}
16Poids: ${weight} kg
17Statut: actif
18`;
19console.log(card);
20
21// 3. Méthodes de recherche
22console.log('=== Recherche ===');
23const dna = 'ATCG-TTGA-ACGT-TTGA';
24console.log('indexOf TTGA:', dna.indexOf('TTGA'));
25console.log('includes ACGT:', dna.includes('ACGT'));
26console.log('startsWith ATCG:', dna.startsWith('ATCG'));
27console.log('endsWith TTGA:', dna.endsWith('TTGA'));
28
29// 4. Découpage et division
30console.log('\n=== Slice et Split ===');
31const specimen = 'Tyrannosaurus Rex';
32console.log('slice(0,13):', specimen.slice(0, 13));
33console.log('slice(-3):', specimen.slice(-3));
34console.log('split:', 'A-B-C-D'.split('-'));
35
36// 5. Transformations
37console.log('\n=== Transformations ===');
38console.log('trim:', '  rex  '.trim());
39console.log('toUpperCase:', 'rex'.toUpperCase());
40console.log('replace:', 'Zone A, Zone B'.replace('Zone', 'Secteur'));
41console.log('replaceAll:', 'Zone A, Zone B'.replaceAll('Zone', 'Secteur'));
42
43// 6. Remplissage (padding)
44console.log('\n=== Padding ===');
45console.log('padStart:', '42'.padStart(5, '0'));
46console.log('padEnd:', 'Rex'.padEnd(10, '.'));
47
48// 7. Chaînage de méthodes
49console.log('\n=== Chaînage ===');
50const messy = '  parc JURASSIQUE  ';
51const clean = messy.trim().toLowerCase();
52console.log('Résultat:', clean);

Retiens bien : un template literal est une étiquette avec des champs libres pour les données, et les méthodes de chaînes sont les scalpels du laborantin, qui ne coupent jamais l'échantillon d'origine mais en créent une copie.

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Quel caractère utilise-t-on pour entourer les littéraux de template en JavaScript ?

  2. 2. Quelle syntaxe permet d'insérer une expression JavaScript à l'intérieur d'un littéral de template ?

  3. 3. Que va retourner 'Tyrannosaurus Rex'.slice(-3) ?

  4. 4. Quelle est la différence entre replace() et replaceAll() en JavaScript ?

  5. 5. Que va retourner 'A-B-C-D'.split('-') ?

Tâches pratiques dans le jeu

  • Clique dans l'ordre

    Range les éléments d'un appel qui vérifie si dnaSequence contient le fragment 'ACGT' :

  • Tri horizontal

    Range les éléments de l'appel de padStart() pour compléter un identifiant avec des zéros :

Articles utiles