1 Panneau Sources & premier débogage (60–75 min)
Pour bien démarrer : reprenez le défi du canon dans Day1_recap. C’est un excellent échauffement pour revoir les notions du jour 1 avant d’ouvrir le débogueur.
Aujourd’hui on apprend à regarder le code s’exécuter, pas seulement à le lire.
Ouvrez les DevTools → onglet Sources.
Vous allez utiliser :
- les breakpoints (clic dans la gouttière)
- Step over / Step into / Step out
- Call stack
- Scope
- Watch
- les breakpoints conditionnels
- l’instruction debugger;
function demo(x) {
debugger; // le navigateur s’arrête ici
return x * 2;
}
demo(21);
À essayer
- Ouvrez le dossier www/day-2/s1_sources-debugger.
- Le fichier scripts.js contient plusieurs fonctions volontairement buggy.
- Votre travail : trouver et comprendre chaque bug avec le débogueur, pas en devinant.
Checkpoint
Vous savez poser un breakpoint, avancer ligne par ligne, inspecter les variables et utiliser un breakpoint conditionnel.
2 Tableaux & méthodes essentielles (60 min)
Les tableaux sont partout.
On se concentre sur les méthodes les plus utiles au quotidien.
- map → transformer chaque élément
- filter → garder certains éléments
- find → trouver le premier qui correspond
- some / every → tester une condition
- reduce et sort : on les voit rapidement (stretch)
const nums = [3, 8, 12, 5];
const doubled = nums.map(n => n * 2); // [6, 16, 24, 10]
const evens = nums.filter(n => n % 2 === 0); // [8, 12]
const firstBig = nums.find(n => n > 10); // 12
const hasBig = nums.some(n => n > 10); // true
const allPositive = nums.every(n => n > 0); // true
À essayer
- Ouvrez le dossier www/day-2/s2_arrays.
- Réécrivez les statistiques d’hier avec les méthodes de tableaux.
- Utilisez le panneau Sources dès que quelque chose ne se comporte pas comme prévu.
Checkpoint
Vous savez enchaîner map + filter et vous utilisez le débogueur dès qu’un résultat est bizarre.
3 Objets, destructuring, spread/rest (45–50 min)
Les objets regroupent des données liées.
Destructuring, spread et rest rendent le code plus lisible.
Points essentiels
- Création et accès (dot notation et crochets)
- Raccourci de propriété
- Destructuring d’objet et de tableau
- Spread (...) pour copier ou fusionner
- Rest pour collecter le reste
const user = { name: "Alex", age: 28 };
// Destructuring
const { name, age, city = "Paris" } = user;
// Spread
const copy = { ...user, city: "Lyon" };
// Rest
function firstAndRest(first, ...rest) {
console.log(first, rest);
}
À essayer
- Ouvrez le dossier www/day-2/s3_objects.
- Suivez les commentaires dans scripts.js.
- Utilisez le panneau Sources dès qu’une valeur est undefined ou inattendue.
Checkpoint
Vous savez destructurer un objet, utiliser spread pour en cloner un, et vous n’êtes plus surpris par une propriété manquante.
4 Closures (niveau pratique) (40–45 min)
Une closure = une fonction + le contexte lexical dans lequel elle a été créée.
Elle “se souvient” des variables extérieures même après que la fonction parente a terminé.
On reste au niveau pratique. Pas de plongée dans les prototypes.
function createCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Remarque : en JavaScript, on peut aussi créer des getters et des setters avec les mots-clés get et set.
Avec une méthode classique :
function createCounter() {
let count = 0;
return {
increment() {
count++;
},
getValue() {
return count;
}
};
}
getValue() peut devenir un getter. La valeur se lit alors avec counter.value, sans parenthèses :
function createCounter() {
let count = 0;
return {
increment() {
count++;
},
get value() {
return count;
}
};
}
const counter = createCounter();
counter.increment();
console.log(counter.value); // 1
À essayer
- Ouvrez le dossier www/day-2/s4_closures.
- Les exercices contiennent des closures utiles et quelques pièges classiques.
- Débuggez avec le panneau Sources (regardez le Scope).
Checkpoint
Vous savez reconnaître une closure et vous avez vu dans le débogueur les variables qu’elle capture.
5 this : comprendre le problème et les solutions (30–40 min)
this dépend de la façon dont la fonction est appelée, pas de l’endroit où elle est écrite.
5.1 Le problème classique
Quand on extrait une méthode, this est perdu :
const user = {
name: "Alex",
greet() {
console.log("Bonjour " + this.name);
}
};
user.greet(); // "Bonjour Alex"
const fn = user.greet;
fn(); // this perdu
5.2 L’ancienne solution : that / self
const user = {
name: "Alex",
greet() {
const that = this;
setTimeout(function () {
console.log("Bonjour " + that.name);
}, 100);
}
};
Ça marchait, mais c’était facile à oublier.
5.3 Amélioration : bind
const fn = user.greet.bind(user);
fn(); // "Bonjour Alex"
5.4 Solution moderne : les arrow functions
Les arrow functions n’ont pas leur propre this.
Elles prennent le this du contexte lexical.
const user = {
name: "Alex",
greet() {
setTimeout(() => {
console.log("Bonjour " + this.name); // this = user
}, 100);
}
};
Plus besoin de that dans la plupart des cas.
Rappel des cas de base
- fn() → this = global ou undefined (strict)
- obj.fn() → this = obj
À essayer
- Ouvrez le dossier www/day-2/s5_this.
- Observez les valeurs de this, reproduisez le problème classique, puis testez bind et les arrow functions.
- Utilisez le panneau Sources (Scope).
Checkpoint
Vous avez vu le problème de la perte de this, l’ancienne solution avec that, et la solution moderne avec les arrow functions.
6 Clonage : shallow vs deep (30–40 min)
Copier un objet n’est pas aussi simple qu’il y paraît.
Shallow copy (copie superficielle)
Seules les propriétés du premier niveau sont copiées.
Les objets imbriqués restent partagés.
const original = { a: { b: { c: { d: 1 } } } };
const shallow = { ...original }; // ou Object.assign({}, original)
shallow.a.b.c.d = 99;
console.log(original.a.b.c.d); // 99 ← modifié aussi !
Deep copy (copie profonde)
Tout est dupliqué. Les modifications sur la copie n’affectent plus l’original.
const original = { a: { b: { c: { d: 1 } } } };
const deep = structuredClone(original);
deep.a.b.c.d = 99;
console.log(original.a.b.c.d); // 1 ← intact
console.log(deep.a.b.c.d); // 99
structuredClone est la solution moderne et recommandée.
JSON.parse(JSON.stringify(obj)) fonctionne dans les cas simples mais perd les Date, undefined, fonctions, etc.
À essayer
- Ouvrez le dossier www/day-2/s6_cloning.
- Reproduisez les exemples, puis explorez la différence en modifiant les valeurs imbriquées.
Checkpoint
Vous savez expliquer pourquoi un spread n’est pas une vraie copie profonde et vous avez utilisé structuredClone.
7 try / catch (25–30 min)
Certaines opérations peuvent échouer.
try/catch permet de gérer l’erreur sans faire planter tout le script.
try {
const data = JSON.parse("{ invalid }"); // → SyntaxError
console.log(data);
} catch (error) {
console.log("Erreur capturée :", error.message);
}
console.log("Le script continue.");
Le code dans try est exécuté normalement.
Si une exception est levée, l’exécution saute dans catch.
Le reste du programme continue.
On peut aussi utiliser finally (optionnel) pour du code qui s’exécute dans tous les cas.
À essayer
- Ouvrez le dossier www/day-2/s7_try-catch.
- Provoquez des erreurs volontaires et capturez-les.
Checkpoint
Vous savez entourer une opération risquée avec try/catch et lire le message d’erreur.
8 switch (20–25 min)
Quand on doit comparer une même valeur à plusieurs possibilités, switch est souvent plus lisible qu’une longue chaîne de if/else.
const day = 3;
switch (day) {
case 1:
console.log("Lundi");
break;
case 2:
console.log("Mardi");
break;
case 3:
console.log("Mercredi");
break;
default:
console.log("Autre jour");
}
Points importants :
breakest nécessaire pour éviter le « fall-through ».defaultgère tous les autres cas.- On peut grouper plusieurs
casesansbreaksi le traitement est identique.
À essayer
- Ouvrez le dossier www/day-2/s8_switch.
- Écrivez un petit
switchet testez le comportement avec et sansbreak.
Checkpoint
Vous savez écrire un switch correct et vous avez vu l’effet d’un break manquant.
Extra 1 — JSON (nice-to-have)
JSON est le format d’échange le plus courant sur le web.
- JSON.stringify(value) → transforme une valeur JS en chaîne JSON
- JSON.parse(text) → transforme une chaîne JSON en valeur JS
const data = { name: "Alex", age: 28 };
const text = JSON.stringify(data);
const back = JSON.parse(text);
const pretty = JSON.stringify(data, null, 2);
console.log(pretty);
Quelques pièges classiques :
- undefined, les fonctions et les symbol sont ignorés ou provoquent des erreurs
- les dates deviennent des chaînes
- les clés d’objet non-string sont converties
À essayer
Ouvrez le dossier www/day-2/x1_json et explorez librement.
Extra 2 — localStorage (nice-to-have)
Le navigateur peut garder des données même après un rafraîchissement.
localStorage.setItem(clé, valeur); // enregistre une chaîne
localStorage.getItem(clé); // récupère la chaîne (ou null)
localStorage.removeItem(clé); // supprime
Tout est stocké sous forme de texte.
Pour un objet, on passe par JSON.
À essayer
- Ouvrez le dossier www/day-2/x2_local-storage.
- Suivez les commentaires, rafraîchissez la page, et observez que les données sont toujours là.
Récap — Exploration d’un store Redux minimal
Terminez la journée en explorant une application de notes déjà fonctionnelle qui réunit les tableaux, les objets, le spread, les closures, JSON et le stockage dans le navigateur.
Ouvrez le dossier Day2_redux dans le dépôt js-extra.
- Lancez l’unique version
lab, puis ouvrez la liste des repèresBP-xxen tête desrc/app.js. - Ajoutez puis sélectionnez une note et comparez « État précédent » avec « État actuel » sous l’application.
- Dans
src/store.js, repérez la closure du store, les actions, le reducer pur et les mises à jour avec spread. - Dans
localStorage, repérezday2_redux_notesetday2_redux_last_note_id: le second conserve le dernier identifiant séquentiel, même après une suppression. - Comparez ensuite avec le compteur de visites dans
sessionStorage, puis rafraîchissez la page.
Le switch du reducer
switch choisit une branche selon la valeur de action.type. Chaque case traite une action et retourne un nouvel état. Comme chaque branche fait un return, aucun break n’est nécessaire. default conserve l’état pour une action inconnue.
export function reducer(state, action) {
switch (action.type) {
case "note/added":
return {
...state,
notes: [...state.notes, action.payload]
};
case "note/selected":
return {
...state,
selectedId: action.payload
};
default:
return state;
}
}
Un premier filet de sécurité : try / catch / finally
JSON.parse lève une erreur si le texte reçu n’est pas un JSON valide. Cette structure permet de garder le contrôle :
tryexécute le code susceptible d’échouer ;catchreçoit l’erreur et permet de réagir proprement ;finallys’exécute dans tous les cas, que l’opération réussisse ou non.
function readImportedNotes(jsonText, fileInput) {
try {
const notes = JSON.parse(jsonText);
return Array.isArray(notes) ? notes : [];
} catch (error) {
console.warn("Le fichier JSON n’est pas valide.", error);
return [];
} finally {
fileInput.value = "";
}
}
Ici, le champ de fichier est vidé même si la lecture échoue. Dans src/storage.js, suivez les repères autour de loadNotes pour observer les trois blocs dans le débogueur.
Suivez le README.md pour lancer npm install puis npm run dev. Il n’y a volontairement pas de corrigé au jour 2 : l’objectif est d’observer le flux complet, de formuler des hypothèses et de les vérifier avec les breakpoints.