JavaScript — Bases

JavaScript est souvent décrit comme le "cerveau" des pages web. C’est le langage qui permet de transformer un document statique en une expérience interactive et dynamique.

Voici un tour d'horizon pour comprendre d'où il vient et ce qu'il a dans le ventre.


📜 Un peu d'histoire : 10 jours pour changer le web

Le JavaScript a été créé en 1995 par Brendan Eich alors qu’il travaillait chez Netscape. La légende (vraie !) raconte qu’il a conçu la première version du langage en seulement 10 jours.

À l'origine, il devait s'appeler Mocha, puis LiveScript. Le nom "JavaScript" a finalement été choisi pour surfer sur la popularité du langage Java à l'époque, bien que les deux n'aient techniquement pas grand-chose en commun. Aujourd'hui, son nom officiel pour la standardisation est ECMAScript, mais tout le monde continue de l'appeler JS.

💡 Pourquoi existe-t-il ?

Avant JavaScript, le web était "mort". Les pages étaient de simples documents texte avec des liens (HTML). Pour voir une mise à jour, il fallait recharger toute la page.

JavaScript a été inventé pour apporter de la logique côté client (directement dans votre navigateur). Son rôle est de manipuler le contenu de la page sans avoir à solliciter le serveur à chaque clic.

Pour bien comprendre, on utilise souvent l'analogie de la construction d'une maison :

  • HTML : La structure (les murs, les fondations).
  • CSS : La décoration (la peinture, les rideaux).
  • JavaScript : L'électricité et la plomberie (les lumières qui s'allument, l'eau qui coule quand on tourne le robinet).

🚀 Ce qu'il permet de faire aujourd'hui

Si JavaScript a commencé comme un petit langage pour faire bouger des images ou valider des formulaires, il est devenu un géant omniprésent :

  1. Interactivité Web : Menus déroulants, cartes interactives (Google Maps), mises à jour de réseaux sociaux en temps réel (le bouton "Like" qui s'incrémente sans recharger la page).
  1. Côté Serveur (Node.js) : Depuis 2009, JS peut aussi être utilisé pour construire toute la partie invisible des sites (bases de données, serveurs), sortant ainsi du simple navigateur.
  1. Applications Mobiles : Avec des outils comme React Native, on peut créer des applications pour iPhone et Android en utilisant du JavaScript.
  1. Jeux Vidéo : De nombreux jeux par navigateur fonctionnent entièrement grâce à lui.

🛠️ Pourquoi est-il si populaire ?

  • Accessibilité : Vous n'avez besoin de rien d'autre qu'un navigateur web et un bloc-notes pour commencer à coder.
  • Universalité : C'est le seul langage de programmation compris nativement par tous les navigateurs (Chrome, Safari, Firefox).
  • Communauté : C'est l'un des langages les plus utilisés au monde, ce qui signifie qu'on trouve de l'aide et des outils (bibliothèques) pour absolument tout.

1) Démarrer rapidement

JavaScript peut s’exécuter:

  • Dans le navigateur (pages web)
  • Avec Node.js (scripts, serveurs, outils)

Mettre du JS dans une page HTML

Tu as 2 façons courantes: inline (dans la page) ou via un fichier.

1) Script inline (rapide pour tester)

<!doctype html>
<html>
  <body>
    <h1>Hello</h1>
    <script>
      console.log('JS chargé');
    </script>
  </body>
</html>

2) Fichier séparé (recommandé)

<script src="./main.js" defer></script>

C’est quoi defer ?

defer indique au navigateur:

  • Télécharge le script pendant le chargement de la page
  • Attends que le HTML soit parsé avant d’exécuter le JS

Résultat: pas besoin de mettre le script tout en bas de la page pour éviter que document.querySelector(...) tombe sur des éléments pas encore créés.

Exemple typique:

<head>
  <script src="./main.js" defer></script>
</head>
<body>
  <button id="btn">OK</button>
</body>

Faut-il ajouter type="text/javascript" ?

En HTML moderne, non: par défaut, <script> est déjà du JavaScript.

  • OK (et conseillé):
<script src="./main.js" defer></script>
  • type="text/javascript" est surtout utile pour compatibilité/explicite, mais généralement inutile.
  • À connaître: type="module" sert pour les modules ES (imports/exports).

Petit exemple DOM (avec defer)

<script src="./main.js" defer></script>
<button id="btn">Clique</button>
// main.js
const btn = document.querySelector('#btn');
btn.addEventListener('click', () => console.log('clic'));

2) Variables et types

  • const pour des références non réassignées
  • let pour des variables réassignables
  • Éviter var

const n’est pas immuable, c’est la RÉFÉRENCE qui est constante

  • Avec const, on ne peut pas réassigner la variable vers une nouvelle valeur
  • Mais si la valeur est un objet ou un tableau, on peut modifier SON CONTENU
// Interdite: réassignation
const pi = 3.14;
// pi = 3.14159; // TypeError: Assignment to constant variable.

// Autorisée: mutation interne d'un tableau
const nums = [1, 2, 3];
nums.push(4); // [1,2,3,4]

// Autorisée: mutation interne d'un objet
const user = { id: 1, name: 'Ada' };
user.name = 'Grace'; // { id:1, name:'Grace' }

// Copie (nouvelle référence) — OK si affectée à une AUTRE variable
const newNums = [...nums, 5];

Points clés:

  • const garantit que l’identifiant pointe toujours vers la même référence
  • Pour des structures immuables, il faut créer de nouvelles copies (spread, Array.map, etc.)

Pourquoi préférer let et const à var

  • Portée:
    • var a une portée de fonction et fuit les blocs {}
    • let et const ont une portée de bloc, plus prévisible
  • Hoisting:
    • var est « hoisted » et initialisé à undefined, ce qui peut masquer des bugs
    • let et const sont hoisted mais non initialisés avant la « temporal dead zone » (TDZ), ce qui empêche les accès prématurés
  • Ré-déclaration:
    • var peut être redéclaré dans la même portée sans erreur
    • let et const ne peuvent pas être redéclarés, ce qui évite des collisions

Exemples:

// var fuit les blocs
if (true) {
  var x = 1;
}
console.log(x); // 1 (fuite de bloc)

// let/const respectent le bloc
if (true) {
  let y = 2;
}
// console.log(y); // ReferenceError

// Hoisting problématique avec var
console.log(a); // undefined (et pas ReferenceError)
var a = 10;

// TDZ protège contre les usages avant déclaration
// console.log(b); // ReferenceError (TDZ)
let b = 10;

// Redéclaration
var k = 1;
var k = 2; // OK mais dangereux
let m = 1;
// let m = 2; // SyntaxError

Exemples supplémentaires

// Mutation sûre d’un objet const
const settings = { theme: 'light', lang: 'fr' };
settings.theme = 'dark';

// Création d’une nouvelle version immuable
const updatedSettings = { ...settings, lang: 'en' };

// Tableaux: map, filter, reduce créent de nouvelles références
const numbers = [1,2,3,4,5];
const odds = numbers.filter(n => n % 2 === 1); // [1,3,5]
const squared = numbers.map(n => n*n); // [1,4,9,16,25]
const total = numbers.reduce((acc,n) => acc+n, 0); // 15

// const + fonctions fléchées
const add = (a, b) => a + b;

// Déstructuration et valeurs par défaut
const user2 = { id: 2 };
const { name = 'Anonyme' } = user2; // 'Anonyme'

// Attention aux références partagées
const arr1 = [1,2];
const arr2 = arr1; // même référence
arr2.push(3);
console.log(arr1); // [1,2,3]

// Cloner pour éviter les effets de bord
const arr3 = [...arr1];
arr3.push(4);

Bonnes pratiques

  • Préférer const par défaut, puis utiliser let si la réassignation est nécessaire
  • Éviter var pour réduire les surprises de portée et de hoisting
  • Privilégier les copies immuables pour les objets/tableaux

3) Chaînes et nombres

Les chaînes (string) représentent du texte. Les nombres (number) couvrent entiers et décimaux et utilisent un format à virgule flottante.

Points clés avant de coder:

  • Chaînes: utilisez les backticks ` pour les template strings avec interpolation et retours à la ligne.
  • Nombres: attention aux erreurs d’arrondi en flottant. Utilisez toFixed, Math.round et des conversions explicites.
  • Conversions: Number() convertit en nombre, parseInt et parseFloat analysent depuis le début de la chaîne.
  • NaN: utilisez Number.isNaN(x) pour tester, pas isNaN qui convertit d’abord.

Chaînes — littéraux, interpolation et méthodes courantes

const a = 'Bonjour';
const b = "Monde";
const name = 'Ada';
const msg = `Hello ${name}, today is ${new Date().toLocaleDateString()}`;
// multi‑ligne avec backticks
const poem = `Roses are red\nViolets are blue`;

// Méthodes utiles
'  JS  '.trim(); // 'JS'
'Ada Lovelace'.toLowerCase(); // 'ada lovelace'
'Ada Lovelace'.includes('love'); // true
'2025-10-16'.split('-'); // ['2025','10','16']
['JS','Vanilla'].join(' '); // 'JS Vanilla'

Nombres — conversions, arrondis, Math

Number('42'); // 42
parseInt('42px', 10); // 42 (interprétation en base 10)
parseInt("0xFF", 16); // 255 (interprétation en base 16)
parseFloat('3.14rad'); // 3.14

// Erreurs d’arrondi en flottant
0.1 + 0.2; // 0.30000000000000004
(0.1 + 0.2).toFixed(2); // '0.30'

// Arrondis
Math.round(2.5); // 3
Math.floor(2.9); // 2
Math.ceil(2.1); // 3

// Min/Max aléatoire
Math.min(3, 7, 2); // 2
Math.max(3, 7, 2); // 7
Math.random(); // 0 <= x < 1

NaN, Infinity et sécurité des checks

Number('abc'); // NaN
Number.isNaN(Number('abc')); // true
isNaN('abc'); // true (attention: isNaN convertit la chaîne avant)
1 / 0; // Infinity
-1 / 0; // -Infinity

Plus d’exemples pratiques

// Formatage décimal contrôlé
const price = 12.3456;
Number(price.toFixed(2)); // 12.35

// Formatage localisé
price.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' });
// '12,35 €'

// Construire une chaîne lisible
const first = 'Ada', last = 'Lovelace';
const full = `${last.toUpperCase()}, ${first}`; // 'LOVELACE, Ada'

// Extraire le domaine d’un email simple
const email = 'ada@example.com';
const domain = email.split('@')[1]; // 'example.com'

// Compter des occurrences
const text = 'banana';
const countA = [...text].filter(ch => ch === 'a').length; // 3

4) Tableaux (arrays)

Un tableau est une liste ordonnée de valeurs (indexées à partir de 0). C’est le type le plus courant pour manipuler des collections (liste d’utilisateurs, produits, messages…).

Points clés:

  • Accès par index: arr[0], arr[arr.length - 1]
  • Les méthodes comme map, filter, reduce créent en général un nouveau tableau (pratique pour éviter les effets de bord)
  • Les méthodes comme push, pop, splice, sort, reverse modifient le tableau (mutation)

Création, accès, modification

const nums = [1, 2, 3];

nums[0]; // 1
nums.length; // 3
nums[nums.length - 1]; // 3

nums.push(4);   // ajoute à la fin (mutation)
nums.unshift(0); // ajoute au début (mutation)
nums.pop();     // retire le dernier (mutation)
nums.shift();   // retire le premier (mutation)

Parcourir (for...of) + index

const names = ['Ada', 'Grace', 'Linus'];
for (const name of names) {
  console.log(name);
}

names.forEach((name, i) => {
  console.log(i, name);
});

Transformer / filtrer / réduire

const xs = [1, 2, 3, 4];

const doubled = xs.map(n => n * 2);        // [2,4,6,8]
const evens = xs.filter(n => n % 2 === 0); // [2,4]
const sum = xs.reduce((acc, n) => acc + n, 0); // 10

Chercher dans un tableau

const users = [
  { id: 1, name: 'Ada' },
  { id: 2, name: 'Grace' },
];

users.find(u => u.id === 2);      // {id:2, name:'Grace'} ou undefined
users.some(u => u.name === 'Ada'); // true
users.every(u => u.id > 0);        // true

const letters = ['a', 'b', 'c'];
letters.includes('b'); // true

Slice vs Splice (très utile à connaître)

  • slice(start, end?): copie une portion (ne mute pas)
  • splice(start, deleteCount, ...items): modifie le tableau
const arr = ['a', 'b', 'c', 'd'];

const part = arr.slice(1, 3); // ['b','c']
console.log(arr);             // ['a','b','c','d']

arr.splice(1, 2, 'X');        // enlève 'b','c' et insère 'X'
console.log(arr);             // ['a','X','d']

Déstructuration et fusion (spread)

const nums2 = [10, 20, 30, 40];
const [first, ...rest] = nums2; // first=10, rest=[20,30,40]

const merged = [...nums2, 50, 60];

// Copier pour éviter la mutation
const copy = [...nums2];

Trier (attention: sort mute et trie comme des strings par défaut)

const scores = [10, 2, 30];

scores.sort(); // [10, 2, 30] (tri "string" => piège)

const sorted = [...scores].sort((a, b) => a - b); // copie + tri numérique

5) Objets

Un objet est une structure de données en paires clé → valeur. C’est parfait pour représenter une « chose » (un utilisateur, un produit, une configuration) avec des propriétés nommées.

Points clés:

  • Accès: notation point obj.key ou crochets obj['key'] (utile si la clé est dynamique)
  • Ajouter / modifier / supprimer une propriété est simple
  • Les objets sont passés par référence: copier avec le spread ({ ...obj }) pour éviter des effets de bord (copie superficielle)

Créer, lire, écrire

const user = { id: 1, name: 'Ada' };

user.name;        // 'Ada'
user['id'];       // 1

user.email = 'ada@example.com'; // ajout
user.name = 'Ada Lovelace';     // modification

delete user.email;              // suppression

Clé dynamique + valeurs par défaut

const settings = { theme: 'dark' };

const key = 'theme';
console.log(settings[key]); // 'dark'

// Valeur par défaut si absent
const lang = settings.lang ?? 'fr';

Déstructuration (extraire des propriétés)

const product = { id: 10, title: 'Keyboard', price: 99, currency: 'EUR' };

const { title, price } = product;

// Renommer + valeur par défaut
const { currency: cur = 'EUR', stock = 0 } = product;

Spread: copier et mettre à jour (immutabilité)

const original = { id: 1, name: 'Ada', role: 'admin' };

const updated = { ...original, role: 'editor' };

// original n'a pas changé

Objet imbriqué (copie superficielle: attention)

const u1 = { name: 'Ada', prefs: { theme: 'dark' } };
const u2 = { ...u1 }; // copie superficielle

u2.prefs.theme = 'light';
console.log(u1.prefs.theme); // 'light' (même sous-objet partagé)

// Solution simple si une seule profondeur:
const u3 = { ...u1, prefs: { ...u1.prefs, theme: 'blue' } };

Parcourir un objet

const scores = { alice: 10, bob: 7, clara: 12 };

Object.keys(scores);   // ['alice','bob','clara']
Object.values(scores); // [10,7,12]
Object.entries(scores);// [['alice',10], ...]

for (const [name, score] of Object.entries(scores)) {
  console.log(name, score);
}

Fusionner des objets (attention: dernière valeur gagne)

const defaults = { theme: 'light', lang: 'fr' };
const custom = { theme: 'dark' };

const merged = { ...defaults, ...custom }; // theme='dark', lang='fr'
  • Références vs copies: objets et tableaux sont passés par référence, utiliser spread pour copier superficiellement

6) Fonctions et portée

Une fonction est un bloc de code réutilisable: tu lui donnes éventuellement des paramètres, et elle renvoie une valeur (ou produit un effet). Comprendre la portée (scope) est crucial: elle détermine où une variable est accessible.

Les différentes façons d’écrire une fonction

Les 3 syntaxes principales font la même chose, mais ont des comportements un peu différents.

// 1) Déclaration de fonction ("function")
function add(a, b) {
  return a + b;
}

// 2) Expression de fonction ("function" assignée à une variable)
const add2 = function (a, b) {
  return a + b;
};

// 3) Fonction fléchée (recommandée dans la plupart des cas)
const add3 = (a, b) => a + b;

Pourquoi on préfère souvent les fonctions fléchées:

  • Plus courtes et lisibles (surtout pour les callbacks: map, filter, événements…)
  • Elles n’ont pas leur propre this (elles capturent le this du scope parent), ce qui évite beaucoup de bugs dans le code moderne
  • Elles s’utilisent naturellement avec const (on évite les réassignations involontaires)

Quand garder function:

  • Quand tu veux une fonction nommée (debug/stack traces) ou un comportement spécifique de this / arguments

Points clés:

  • Une fonction peut être déclarée de plusieurs façons (déclaration, expression, fléchée)
  • Les paramètres peuvent avoir des valeurs par défaut et accepter un nombre variable d’arguments (...rest)
  • Une fonction « voit » les variables de son scope et des scopes parents (closure)
  • let / const ont une portée de bloc, ce qui rend le scope plus prévisible
  • Déclaration, expression, fléchées
const add = (a, b) => a + b;
const mul = (a, b) => a * b;
const sub = (a, b) => a - b;

add(2, 3);        // 5
add(-1, 4);       // 3
add(2.5, 0.75);   // 3.25
mul(4, 5);        // 20
mul(-3, 6);       // -18
mul(2.5, 2);      // 5
sub(10, 7);       // 3
sub(0, 5);        // -5
sub(2.5, 0.5);    // 2

// Composition simple
add(mul(2, 3), sub(10, 4)); // add(6, 6) -> 12

// Avec tableaux et map: appliquer add partiellement
const xs = [1, 2, 3];
const plusDeux = x => add(x, 2);

xs.map(plusDeux); // [3, 4, 5]
  • Paramètres par défaut, rest
    • greet(name = 'world') utilise un paramètre par défaut. Si aucun argument n’est fourni, name prend la valeur 'world'. Cela évite d’avoir à écrire name = name ?? 'world' dans le corps de la fonction.
    • sumAll(...xs) utilise le paramètre « rest ». La syntaxe ...xs dans la DÉCLARATION de fonction regroupe tous les arguments restants dans un tableau appelé xs. On peut ensuite itérer, réduire, filtrer ce tableau. Exemple, appeler sumAll(1,2,3) donne xs = [1,2,3].
    • Ne pas confondre « rest » et « spread »: ... en DÉCLARATION de fonction (rest) collecte des valeurs dans un tableau. ... en APPEL ou en LITTÉRAL (spread) déplie un tableau ou un objet. Par exemple sumAll(...[1,2,3]) utilise le spread pour passer 3 arguments séparés.
// Paramètre par défaut: si aucun prénom fourni, on salue 'world'
const greet = (name = 'world') => `Hello ${name}`;

// Rest: accepte un nombre variable d’arguments
const sumAll = (...xs) => xs.reduce((a, n) => a + n, 0);

greet(); // 'Hello world'
greet('Ada'); // 'Hello Ada'

sumAll(1); // 1
sumAll(1, 2, 3); // 6

// Rest vs Spread
const nums = [1, 2, 3];
sumAll(...nums); // 6  (spread côté appel)

// Combiner valeur obligatoire + rest
const labelSum = (label, ...values) => {
  const total = values.reduce((a, n) => a + n, 0);
  return `${label}: ${total}`;
};
labelSum('Total', 5, 10, 15); // 'Total: 30'
const greet = (name = 'world') => `Hello ${name}`;
const sumAll = (...xs) => xs.reduce((a, n) => a + n, 0);
  • Portée de bloc et closures
const makeCounter = () => {
  let x = 0;
  return () => ++x;
};
const next = makeCounter();
next(); // 1

7) DOM et événements (Vanilla)

Le DOM (Document Object Model) est la représentation de la page HTML sous forme d’objets JavaScript. Manipuler le DOM = lire / modifier le contenu, les attributs et les styles, et réagir aux actions utilisateur via des événements.

Points clés:

  • Sélectionner des éléments: getElementById, querySelector, querySelectorAll
  • Modifier: textContent, innerHTML, attributs (setAttribute), classes (classList)
  • Créer/insérer/supprimer: createElement, append, prepend, remove
  • Écouter des événements: addEventListener (click, input, submit, keydown…)

Sélection (querySelector) + modification

<div id="app"></div>
<script>
  const app = document.querySelector('#app');
  app.textContent = 'Bonjour DOM';
</script>

Modifier classes / attributs

const box = document.querySelector('.box');

box.classList.add('active');
box.classList.remove('active');
box.classList.toggle('active');

box.setAttribute('aria-hidden', 'true');
box.getAttribute('aria-hidden');

Manipuler le style (à utiliser avec parcimonie)

const title = document.querySelector('h1');

title.style.color = 'tomato';
title.style.fontSize = '32px';

Créer et insérer des éléments

const list = document.querySelector('#list');

const li = document.createElement('li');
li.textContent = 'Nouveau point';

list.append(li); // ajoute à la fin

Exemple: rendre une liste depuis un tableau

<ul id="todos"></ul>
<script>
  const todos = ['Apprendre DOM', 'Faire un mini-projet', 'Comprendre events'];
  const ul = document.querySelector('#todos');

  ul.innerHTML = todos.map(t => `<li>${t}</li>`).join('');
</script>

Événements: click + prévention par défaut (submit)

<form id="form">
  <input id="name" placeholder="Ton nom" />
  <button>Envoyer</button>
</form>
<script>
  const form = document.querySelector('#form');
  const input = document.querySelector('#name');

  form.addEventListener('submit', (e) => {
    e.preventDefault();
    console.log('Nom:', input.value);
  });
</script>

Délégation d'événements (très pratique pour des listes dynamiques)

<ul id="list"></ul>
<script>
  const ul = document.querySelector('#list');
  ul.innerHTML = ['A', 'B', 'C'].map(x => `<li><button class="remove" data-value="${x}">Supprimer ${x}</button></li>`).join('');

  ul.addEventListener('click', (e) => {
    const btn = e.target.closest('.remove');
    if (!btn) return;

    const value = btn.dataset.value; // 'A', 'B', ...
    btn.closest('li').remove();
    console.log('Supprimé:', value);
  });
</script>

8) Stockage Web: localStorage et sessionStorage

Le Web Storage permet de stocker de petites données côté navigateur sous forme de paires clé → valeur (des strings). Deux stockages existent:

  • localStorage: persiste même après fermeture/relance du navigateur
  • sessionStorage: persiste seulement pendant l’onglet (ou la fenêtre) en cours

Points clés:

  • Les valeurs sont des strings → pour stocker des objets/tableaux, on utilise JSON.stringify / JSON.parse
  • Limité à quelques Mo (selon navigateur)
  • Accessible depuis le JS de la page (donc pas adapté à des secrets)

Exemples de base (set/get/remove/clear)

// Écrire
localStorage.setItem('theme', 'dark');
sessionStorage.setItem('draft', 'Bonjour');

// Lire
const theme = localStorage.getItem('theme'); // 'dark' ou null

// Supprimer une clé
localStorage.removeItem('theme');

// Vider tout le stockage
sessionStorage.clear();

Stocker un objet (JSON)

const user = { id: 1, name: 'Ada', roles: ['admin'] };
localStorage.setItem('user', JSON.stringify(user));

const raw = localStorage.getItem('user');
const parsed = raw ? JSON.parse(raw) : null;
console.log(parsed?.name); // 'Ada'

Petit exemple pratique: garder un formulaire en brouillon (sessionStorage)

const input = document.querySelector('#message');

// Restaurer au chargement
input.value = sessionStorage.getItem('draftMessage') ?? '';

// Sauvegarder à chaque saisie
input.addEventListener('input', () => {
  sessionStorage.setItem('draftMessage', input.value);
});

// Optionnel: effacer quand on « envoie »
function submit() {
  // ... envoi
  sessionStorage.removeItem('draftMessage');
}

9) Asynchrone, fetch et JSON

Comprendre l’asynchrone en JavaScript est essentiel pour les appels réseau, les timers et les opérations coûteuses. Deux concepts clés: les Promises et la syntaxe async/await.

Promises — l’idée

  • Une Promise représente une opération en cours qui aboutira plus tard avec une valeur (résolue) ou une erreur (rejetée).
  • États: pending → fulfilled ou rejected.
  • On consomme une Promise avec .then(...) pour la réussite et .catch(...) pour l’échec.
// Exemple: une Promise qui résout après un délai
const delay = ms => new Promise((resolve) => setTimeout(resolve, ms));

delay(200).then(() => console.log('200 ms passées')).catch(console.error);

async/await — sucre syntaxique sur les Promises

  • async marque une fonction comme asynchrone. Elle retourne toujours une Promise.
  • await suspend l’exécution de la fonction async jusqu’à résolution de la Promise attendue, puis renvoie sa valeur.
  • Avantage: code plus lisible et linéaire que les chaînes de .then(...).
async function demo() {
  await delay(200); // attend 200 ms
  return 'ok';      // implicite: Promise résolue avec 'ok'
}

demo().then(console.log); // 'ok'

fetch — faire une requête HTTP

  • fetch(url, options?) retourne une Promise d’un objet Response.
  • response.ok indique succès HTTP (statut 200–299).
  • Pour lire le corps: response.json(), response.text(), etc. Ces méthodes sont aussi asynchrones et retournent des Promises.
async function getPosts() {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts');
  if (!response.ok) {
    // Par exemple 404, 500...
    throw new Error('HTTP ' + response.status);
  }
  const data = await response.json(); // parse JSON → objet JS
  return data; // tableau de posts
}

try / catch / finally — gérer les erreurs avec await

  • try { ... } catch (err) { ... } permet d’attraper les erreurs lancées avec throw ou issues d’une Promise rejetée attendue avec await.
  • finally { ... } s’exécute toujours, succès ou erreur, pratique pour nettoyer l’UI, arrêter un spinner, etc.
async function showPosts() {
  const listEl = document.getElementById('posts');
  const spinner = document.getElementById('spinner');
  spinner.style.display = 'block';
  try {
    const posts = await getPosts();
    listEl.innerHTML = posts.slice(0, 5).map(p => `<li>${p.title}</li>`).join('');
  } catch (err) {
    console.error(err);
    listEl.textContent = 'Erreur de chargement';
  } finally {
    spinner.style.display = 'none';
  }
}

Bonnes pratiques asynchrone

  • Toujours vérifier response.ok avant de lire le corps.
  • Enchaîner plusieurs awaits dans un try/catch unique quand ils dépendent les uns des autres.
  • Pour lancer plusieurs requêtes indépendantes plus vite, utiliser Promise.all([...]).
async function loadBoth() {
  const [users, posts] = await Promise.all([
    fetch('https://jsonplaceholder.typicode.com/users').then(r => r.json()),
    fetch('https://jsonplaceholder.typicode.com/posts').then(r => r.json()),
  ]);
  console.log(users.length, posts.length);
}

10) Modules ES

Les modules ES (ECMAScript Modules, ESM) sont le standard moderne pour découper ton code JavaScript en plusieurs fichiers, puis importer / exporter des fonctions, variables ou classes entre ces fichiers.

Pourquoi c’est utile:

  • Organisation: un fichier = une responsabilité (ex: utils.js, api.js, dom.js)
  • Réutilisation: tu exportes des morceaux de code et tu les réutilises ailleurs
  • Encapsulation: ce qui n’est pas exporté reste privé au module
  • Chargement fiable: les imports sont résolus avant l’exécution (plus prévisible)

Export / import (le cas le plus courant)

// utils.js
export const capitalize = s => s[0].toUpperCase() + s.slice(1);

// main.js
import { capitalize } from './utils.js';
console.log(capitalize('vanilla'));

Export par défaut (default export)

Utile quand un fichier expose « une chose principale ».

// logger.js
export default function log(msg) {
  console.log('[LOG]', msg);
}

// main.js
import log from './logger.js';
log('hello');

Points importants

  • En navigateur, il faut utiliser type="module":
<script type="module" src="./main.js"></script>
  • Les imports utilisent en général un chemin relatif (./ ou ../) et, selon l’environnement, l’extension .js est requise.
  • Chaque module a sa propre portée (scope): pas de variables globales « par accident ».