Apprendre HTML CSS JavaScript et TypeScript
Tous les éléments HTML, propriétés CSS, cours JavaScript et TypeScript avec exemples dépliants et explications détaillées. Cliquez sur chaque carte pour voir le rôle, les détails et le code.
Structure de base
HTMLLes éléments fondamentaux qui composent la structure de toute page HTML. Cliquez pour développer.
Important : Ce n'est pas une balise HTML mais une instruction de traitement.
lang définit la langue — essentiel pour l'accessibilité (lecteurs d'écran) et le SEO.Attributs :
lang="fr", lang="en", dir="ltr" (left to right).Contient :
<title>, <meta>, <link>, <style>, <script>.<body> par document.Evénements JS : Préférez
document.addEventListener('DOMContentLoaded', ...) plutôt que l'attribut onload.Quand utiliser : Pour les regroupements purement stylistiques. Préférez les balises sémantiques (
<section>, <article>) quand le contenu a un sens.Équivalent du <div> : Le
<div> est block, le <span> est inline. Même règle : pas de valeur sémantique, donc privilégier les balises spécifiques quand c'est possible.Texte & Titres
HTMLEléments pour structurer le texte et mettre en forme le contenu textuel. Cliquez pour développer.
h1 (plus important) à h6 (moins important). Créent la hiérarchie du document. Un seul h1 par page pour le SEO — c'est le titre principal.SEO : Google utilise ces balises pour comprendre la structure. Ne sautez pas de niveaux (h1 → h3 sans h2). N'utilisez pas les titres pour leur taille visuelle — utilisez CSS pour ça.
<h2>Section principale</h2>
<h3>Sous-section</h3>
<h4>Point de détail</h4>
<!-- h5, h6 rarement nécessaires -->
Ne pas faire : Ne jamais utiliser des
<br> répétés pour créer de l'espace entre du texte. Utiliser plusieurs <p> + CSS margin-bottom à la place.<p>Deuxième paragraphe avec <strong>texte
important</strong> et <em>emphase</em>.</p>
Différence <b> :
<b> est purement visuel. <strong> transmet une importance sémantique. Utilisez <strong> pour les avertissements, prix, termes clés.<p>Prix : <strong>29 €</strong>/mois</p>
Différence <i> :
<i> est purement visuel (italique stylistique). <em> = accentuation orale. Utilisez <i> pour les termes étrangers, titres d'œuvres, termes techniques.<p>L'opéra <i>La Traviata</i></p>
<u> : Souligné. Utilisé pour les annotations (ex: fautes orthographiques), erreurs dans un texte. Évitez d'utiliser pour des liens — ça prête à confusion.
<s> : Barré. Indique un contenu plus pertinent (ex: ancien prix).
<p><u>ereur</u> detectée</p>
<p><s>59 €</s> → <strong>29 €</strong></p>
Personnalisation :
mark { background: var(--cyan); color: #000; }<p>Voir la section <mark>3.2</mark> pour plus de détails.</p>
Usage SaaS typique : Conditions de vente, mentions RGPD, prix avec astérisques, disclaimers.
<strong>29 €</strong>/mois
<small>TVA incluse, sans engagement</small>
</div>
<pre> : Texte préformaté — conserve tous les espaces et retours à la ligne. Souvent utilisé avec
<code> pour les blocs multi-lignes.Coloration syntaxique : Ajoutez Prism.js ou Highlight.js pour coloriser automatiquement le code.
<p>Utilisez <code>console.log()</code> pour déboguer.</p>
<!-- Bloc -->
<pre><code class="language-js">
const x = 42;
console.log(x); // 42
</code></pre>
cite contient l'URL de la source (utile pour le SEO et l'accessibilité).Composition : Associez-le à
<cite> pour nommer l'auteur/source, et <p> pour le texte.<blockquote cite="https://...">
<p>Coder, c'est résoudre des problèmes
élégamment.</p>
</blockquote>
<figcaption>— <cite>Martin Fowler</cite></figcaption>
</figure>
title contient la forme développée — affichée en tooltip au survol.Accessibilité : Essentiel pour les lecteurs d'écran. La première occurrence d'une abréviation dans une page devrait toujours être développée avec
<abbr>.<abbr title="HyperText Markup Language">HTML</abbr>
est le langage de la toile.
</p>
<abbr title="Cascading Style Sheets">CSS</abbr>
<hr> : Séparateur horizontal thématique. Indique un changement de sujet dans la page. Personnalisable avec CSS (
border: none; border-top: 1px solid...).<address>
JLQDev<br>
12 rue du Code<br>
75001 Paris
</address>
<hr> <!-- changement de section -->
<sup> (superscript) : texte en exposant, surélevé. Pour puissances (x²), ordinals (1ᵉʳ), notes de bas de page (réf.¹).
<p>Eau : H<sub>2</sub>O</p>
<!-- Mathématiques -->
<p>x<sup>2</sup> + y<sup>2</sup> = z<sup>2</sup></p>
<!-- Ordinal -->
<p>Le 1<sup>er</sup> prix</p>
Listes
HTMLLes différents types de listes pour organiser le contenu.
<li>. Utilisez-la quand l'ordre n'a pas d'importance.Style CSS : Personnalisez les puces avec
list-style-type: disc/circle/square/none, ou créez des puces personnalisées avec ::before.<li>HTML — Structure</li>
<li>CSS — Mise en forme</li>
<li>JavaScript — Interactivité</li>
</ul>
Attributs :
start (commence à un autre numéro), reversed (ordre décroissant), type (1, A, a, I, i pour le format de numérotation).<li>Ouvrir le terminal</li>
<li>Lancer <code>npm install</code></li>
<li>Démarrer le serveur</li>
</ol>
<!-- Liste commençant à 5 -->
<ol start="5">...</ol>
<ul> ou <ol>. Peut contenir n'importe quel contenu HTML, y compris une liste imbriquée.Dans ol : L'attribut
value permet de forcer le numéro d'un item spécifique.<li>Item simple</li>
<li>
Item avec sous-liste
<ul>
<li>Sous-item</li>
</ul>
</li>
</ul>
<dt> : Terme à définir (term).
<dd> : Définition/description du terme.
Cas d'usage : Glossaires, FAQ, métadonnées (auteur : X, date : Y), caractéristiques de produit, dictionnaires.
<dt>HTML</dt>
<dd>HyperText Markup Language</dd>
<dt>CSS</dt>
<dd>Cascading Style Sheets</dd>
</dl>
Liens & Médias
HTMLEléments pour créer des liens, insérer images, vidéos et contenus embarqués.
href définit la destination.Valeurs de href : URL absolue, URL relative,
#ancre, mailto:email, tel:+33..., javascript:void(0).target='_blank' : Ajouter toujours
rel="noopener noreferrer" pour des raisons de sécurité (évite le tabnapping).<a href="https://..." target="_blank"
rel="noopener noreferrer">Visiter</a>
<!-- Email / Téléphone -->
<a href="mailto:contact@jlq.fr">Email</a>
<a href="tel:+33600000000">Appeler</a>
<!-- Téléchargement -->
<a href="/doc.pdf" download>Télécharger</a>
alt est obligatoire pour l'accessibilité et le SEO — décrit l'image pour les lecteurs d'écran et si l'image ne charge pas.Performance : Utilisez
loading="lazy" pour les images sous la ligne de flottaison, width et height pour éviter le CLS (layout shift), et les formats WebP/AVIF pour la compression.src="./assets/hero.webp"
alt="Dashboard de l'application NovaDash"
width="800"
height="450"
loading="lazy"
>
<source> pour la compatibilité entre navigateurs.Attributs importants :
•
controls : affiche les contrôles de lecture•
autoplay + muted : lecture automatique silencieuse•
loop : lecture en boucle•
poster : image d'aperçu avant la lecturecontrols
poster="thumbnail.jpg"
width="640"
>
<source src="video.webm" type="video/webm">
<source src="video.mp4" type="video/mp4">
Votre navigateur ne supporte pas la vidéo.
</video>
Structure : Un ou plusieurs
<source> + un <img> de fallback (obligatoire).<!-- Format moderne AVIF -->
<source srcset="hero.avif" type="image/avif">
<!-- WebP pour navigateurs modernes -->
<source srcset="hero.webp" type="image/webp">
<!-- Fallback JPEG -->
<img src="hero.jpg" alt="Hero">
</picture>
<figcaption> : Légende optionnelle de la figure. Peut être avant ou après le contenu.
<img src="chart.png" alt="Graphique CA 2026">
<figcaption>
Figure 1 — Évolution du CA au T1 2026
</figcaption>
</figure>
Sécurité : L'attribut
sandbox restreint les actions du contenu embarqué (scripts, formulaires). Essentiel pour le contenu tiers non maîtrisé.<iframe
src="https://www.youtube.com/embed/ID"
title="Titre de la vidéo"
width="560" height="315"
allow="accelerometer; autoplay"
allowfullscreen
></iframe>
Contextes :
'2d' pour dessiner des formes/images, 'webgl' ou 'webgl2' pour la 3D accélérée GPU.const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#00d2ff';
ctx.fillRect(10, 10, 100, 50);
// Dessine un rectangle cyan
Avantages : Stylisable en CSS (
fill, stroke), accessible, animable, léger. viewBox définit le système de coordonnées interne.<circle cx="12" cy="12" r="10"
stroke="currentColor" stroke-width="2"/>
<path d="M12 6v6l4 2"
stroke="currentColor" stroke-width="2"/>
</svg>
Tableaux
HTMLStructure complète des tableaux HTML pour afficher des données tabulaires.
Structure complète : table → caption, thead, tbody, tfoot → tr → th/td. Chaque niveau a un rôle sémantique.
<caption>Statistiques 2026</caption>
<thead>
<tr><th>Mois</th><th>CA</th></tr>
</thead>
<tbody>
<tr><td>Janvier</td><td>12 000 €</td></tr>
</tbody>
</table>
<th>). Répété sur chaque page imprimée.<tbody> : Corps — contient les données. Scrollable indépendamment avec CSS (
overflow-y: auto).<tfoot> : Pied — contient les totaux, résumés. Affiché en bas, mais peut être déclaré avant
<tbody> en HTML.<thead><tr>
<th scope="col">Produit</th>
<th scope="col">Prix</th>
</tr></thead>
<tfoot><tr>
<td>Total</td><td>290 €</td>
</tr></tfoot>
</table>
scope="col" ou scope="row" améliore l'accessibilité pour les lecteurs d'écran.<td> : Cellule de données. Fusion avec
colspan (fusionne des colonnes) ou rowspan (fusionne des lignes).<th scope="row">Janvier</th>
<td>12 000 €</td>
<td>+15%</td>
</tr>
<!-- Fusion de 3 colonnes -->
<td colspan="3">Sous-total</td>
<!-- Fusion de 2 lignes -->
<td rowspan="2">Groupe A</td>
Formulaires
HTMLTous les éléments pour créer des formulaires interactifs.
Attributs :
•
action : URL de destination (serveur qui reçoit les données)•
method : GET (dans l'URL, visible) ou POST (dans le body, caché)•
enctype : multipart/form-data obligatoire pour les fichiers•
novalidate : désactive la validation nativeaction="/api/register"
method="POST"
enctype="multipart/form-data"
>
<!-- champs -->
<button type="submit">Envoyer</button>
</form>
Types courants :
text, email, password, number, tel, url, date, search, file, checkbox, radio, range, color, hidden, submit.Attributs clés :
placeholder, required, disabled, readonly, min/max, pattern, autocomplete.name="email"
placeholder="votre@email.com"
required
autocomplete="email"
>
<input type="range" min="0" max="100" step="5">
<input type="color" value="#00d2ff">
Association : Via l'attribut
for qui correspond à l'id du champ, ou en englobant directement le champ.<label for="email">Email *</label>
<input type="email" id="email" name="email">
<!-- Méthode 2 : englobant -->
<label>
Mot de passe
<input type="password" name="pwd">
</label>
<input>, il a une balise fermante et peut contenir une valeur par défaut entre ses balises.Redimensionnement : Contrôlé en CSS avec
resize: none / vertical / horizontal / both.<textarea
id="msg"
name="message"
rows="6"
placeholder="Décrivez votre besoin..."
maxlength="500"
></textarea>
multiple permet la sélection multiple (avec Ctrl/Cmd).<option> : Chaque choix. L'attribut
value est la valeur envoyée au serveur (différente du texte affiché).<optgroup> : Groupe des options en catégories visuelles.
<option value="">-- Choisir --</option>
<optgroup label="Europe">
<option value="fr">France</option>
<option value="de">Allemagne</option>
</optgroup>
</select>
<input type='button'> car il peut contenir du HTML (icônes, badges, etc.).Types :
•
type='submit' : soumet le formulaire (défaut dans un form)•
type='button' : ne soumet pas — action via JS•
type='reset' : réinitialise tous les champs<button type="submit">
Créer un compte
</button>
<!-- Action JS avec icône -->
<button type="button" onclick="toggle()">
<svg>...</svg> Menu
</button>
<legend> : Titre du groupe de champs — affiché dans la bordure du fieldset. Essentiel pour l'accessibilité.
<legend>Informations personnelles</legend>
<label for="prenom">Prénom</label>
<input type="text" id="prenom" name="prenom">
<label for="nom">Nom</label>
<input type="text" id="nom" name="nom">
</fieldset>
value, affiche une animation indéterminée.<meter> : Jauge pour une valeur dans un intervalle connu (disque, score, température). Colore automatiquement selon
low, high, optimum.<progress value="70" max="100">70%</progress>
<!-- Espace disque utilisé -->
<meter
value="7" min="0" max="10"
low="3" high="8" optimum="5"
>7 Go / 10 Go</meter>
<input>. Le lien se fait via l'attribut list de l'input qui doit correspondre à l'id du datalist.Différence avec select : L'utilisateur peut saisir n'importe quelle valeur — les options ne sont que des suggestions, pas des contraintes.
<input
id="framework"
list="frameworks"
placeholder="Tapez ou choisissez..."
>
<datalist id="frameworks">
<option value="React">
<option value="Angular">
<option value="Vue.js">
</datalist>
Eléments Sémantiques
HTMLLes balises sémantiques HTML5 qui améliorent le SEO et l'accessibilité.
<article> peut avoir son propre <header>).Contenu typique : Logo, navigation principale, barre de recherche, titre de section, breadcrumbs.
<a href="/" class="logo">NovaDash</a>
<nav>...</nav>
<button>Connexion</button>
</header>
Multiple nav : Si vous en avez plusieurs, utilisez
aria-label pour les différencier.<ul>
<li><a href="/">Accueil</a></li>
<li><a href="/tarifs">Tarifs</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
</nav>
<main> par page. Les lecteurs d'écran permettent de sauter directement au <main>.Ne doit pas contenir : Les éléments répétés sur plusieurs pages comme header, sidebar, footer, navigation.
<header>...</header>
<nav>...</nav>
<main>
<h1>Dashboard</h1>
<!-- Contenu unique de la page -->
</main>
<footer>...</footer>
</body>
<h2> à <h6>).Quand utiliser section vs div :
<section> si le contenu serait listable dans un sommaire. <div> pour du groupement purement stylistique.<section id="features">
<h2>Fonctionnalités</h2>
<div class="features-grid">...</div>
</section>
<section id="pricing">
<h2>Tarifs</h2>...
</section>
</main>
Test : Ce contenu serait-il sensé dans un feed RSS ou partagé sur les réseaux sociaux ? Si oui →
<article>.<h2>Derniers articles</h2>
<article>
<h3>TypeScript pour débutants</h3>
<p>...</p>
<footer>
<time datetime="2026-02-22">22 fév 2026</time>
</footer>
</article>
</section>
Attention : Ce n'est pas uniquement pour les sidebars — un bloc de contenu connexe dans un article est aussi un
<aside>.<main>...</main>
<aside class="sidebar">
<h3>Articles liés</h3>
<ul>...</ul>
<h3>Newsletter</h3>
<form>...</form>
</aside>
</div>
<header>, peut être utilisé plusieurs fois (ex: chaque article a son footer avec auteur/date).Contenu typique : Copyrights, liens légaux, coordonnées, sitemap, réseaux sociaux.
<div class="footer-links">
<a href="/privacy">Confidentialité</a>
<a href="/terms">CGU</a>
</div>
<p><small>© 2026 JLQDeveloppement</small></p>
</footer>
datetime contient la valeur dans un format standardisé (ISO 8601).Formats datetime :
2026-02-22 (date), 14:30 (heure), 2026-02-22T14:30:00 (datetime), PT2H30M (durée).<h2>Lancement TypeScript 6</h2>
<p>Publié le
<time datetime="2026-02-22">
22 février 2026
</time>
</p>
</article>
open le rend ouvert par défaut.<summary> : Le titre cliquable visible quand le widget est fermé. Doit être le premier enfant de
<details>.<summary>Comment résilier ?</summary>
<p>Rendez-vous dans Paramètres → Abonnement → Résilier.</p>
</details>
<!-- Ouvert par défaut -->
<details open>
<summary>FAQ ouverte</summary>
<p>Réponse visible dès le chargement.</p>
</details>
Head & Meta
HTMLMétadonnées, SEO, Open Graph et tout ce qui va dans le <head>.
Bonnes pratiques : 50-60 caractères max, format
Mot-clé principal — Nom du site. Unique sur chaque page.<title>Tableau de bord — NovaDash</title>
<!-- Pour page d'article -->
<title>TypeScript pour débutants : le guide complet 2026 — JLQDev</title>
</head>
UTF-8 pour supporter tous les caractères (accents, emojis, caractères asiatiques, etc.).Position : Doit être la toute première balise dans le
<head>. Si absent, le navigateur devine l'encodage, ce qui peut causer des problèmes d'affichage.<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>...</title>
</head>
content :
width=device-width = largeur du viewport = largeur de l'écran. initial-scale=1.0 = zoom initial à 100%.content="width=device-width, initial-scale=1.0">
<!-- Désactiver le zoom utilisateur (déconseillé) -->
<meta name="viewport"
content="width=device-width, initial-scale=1.0,
maximum-scale=1.0, user-scalable=no">
Bonnes pratiques : 150-160 caractères max, inclure les mots-clés naturellement, appel à l'action, unique par page.
content="NovaDash est un tableau de bord
financier tout-en-un. Gérez votre
budget, crypto et investissements.
Essai gratuit 30 jours.">
Indispensable : Sans ces balises, les réseaux sociaux choisissent aléatoirement une image de la page.
<meta property="og:description" content="...">
<meta property="og:image" content="https://.../og.png">
<meta property="og:url" content="https://novadash.fr">
<meta property="og:type" content="website">
<!-- Twitter/X spécifique -->
<meta name="twitter:card" content="summary_large_image">
rel courants :
stylesheet, icon, preconnect, preload, canonical, manifest.<link rel="stylesheet" href="style.css">
<!-- Favicon -->
<link rel="icon" href="/favicon.ico">
<!-- Préconnexion Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<!-- URL canonique SEO -->
<link rel="canonical" href="https://...">
Box Model
CSSLe modèle de boîte définit comment chaque élément occupe l'espace.
box-sizing: border-box, ils incluent padding et bordure.Valeurs modernes :
•
min-content : taille minimale que le contenu peut prendre•
max-content : taille que prendrait le contenu sans contrainte•
fit-content : s'adapte au contenu sans dépasser le conteneurwidth: 300px;
height: 200px;
/* Responsive */
width: min(90%, 1200px);
height: 100vh;
/* Adaptatif */
width: fit-content;
min-width: 200px;
max-width: 600px;
Raccourcis :
•
padding: 10px = 4 côtés identiques•
padding: 10px 20px = haut/bas | gauche/droite•
padding: 10px 20px 15px 25px = haut | droite | bas | gauche (sens horaire)padding: 16px;
/* Vertical | Horizontal */
padding: 12px 24px;
/* Individuel */
padding-top: 20px;
padding-inline: 16px; /* gauche+droite */
padding-block: 8px; /* haut+bas */
Astuces :
•
margin: 0 auto : centre horizontalement un bloc avec width•
margin-top: auto : pousse vers le bas dans un flex column• Marges qui collapsent : marges verticales adjacentes fusionnent
width: 800px;
margin: 0 auto;
/* Pousser vers le bas en flex */
margin-top: auto;
/* Supprimer le margin défaut */
margin: 0;
border-width, border-style et border-color. La bordure s'ajoute à la taille de l'élément (sauf avec box-sizing: border-box).Styles :
solid, dashed, dotted, double, groove, none.border: 1px solid rgba(255,255,255,0.1);
/* Bordure individuelle */
border-bottom: 2px solid var(--cyan);
/* Bordure avec dégradé */
border: 1px solid transparent;
background-image: linear-gradient(#000, #000),
linear-gradient(to right, cyan, violet);
background-clip: padding-box, border-box;
50%), des pilules (999px), ou des formes asymétriques.Formes courantes :
•
4px : légère courbure (cards)•
8-16px : fortement arrondi•
999px : pilule (badges, boutons)•
50% : cercle parfaitborder-radius: 12px;
/* Badge/Pill */
border-radius: 999px;
/* Avatar circulaire */
border-radius: 50%;
/* Asymétrique */
border-radius: 20px 4px 20px 4px;
content-box), un élément de width: 100px + padding: 20px fait 140px en réalité.Solution :
border-box inclut padding et border dans la taille déclarée — l'élément fait exactement width: 100px même avec du padding.*, *::before, *::after {
box-sizing: border-box;
}
/* Résultat : width: 200px reste 200px */
.card {
width: 200px;
padding: 20px; /* ne s'ajoute pas */
}
x y blur spread color. La valeur inset crée une ombre intérieure.Effets SaaS :
• Ombre douce :
0 4px 20px rgba(0,0,0,0.3)• Glow néon :
0 0 20px rgba(0,210,255,0.5)• Multiple : séparées par virgule
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
/* Glow néon cyan */
box-shadow: 0 0 30px rgba(0,210,255,0.4);
/* Ombres multiples */
box-shadow:
0 2px 4px rgba(0,0,0,0.2),
0 8px 30px rgba(0,210,255,0.3);
/* Inset (intérieur) */
box-shadow: inset 0 2px 4px rgba(0,0,0,0.5);
Valeurs :
•
visible : dépasse (défaut)•
hidden : coupe le débordement — utile pour border-radius sur des images•
scroll : toujours visible les scrollbars•
auto : scrollbars uniquement si nécessaireoverflow: hidden;
/* Zone scrollable verticalement */
height: 400px;
overflow-y: auto;
/* Couper le texte (avec ellipsis) */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
Typographie CSS
CSSToutes les propriétés pour contrôler la police, la taille et l'apparence du texte.
Familles génériques :
serif, sans-serif, monospace, cursive, fantasy.font-family: 'Outfit', 'Helvetica Neue', sans-serif;
/* Monospace pour le code */
font-family: 'JetBrains Mono', 'Consolas', monospace;
/* Variable system font stack */
font-family: system-ui, -apple-system, sans-serif;
Recommandations :
•
rem : relatif à la taille racine (root). 1rem = 16px par défaut. Respecte les préférences de zoom utilisateur.•
em : relatif au parent — peut créer des effets en cascade•
clamp(min, ideal, max) : taille fluide responsive sans media queryfont-size: 1rem; /* = 16px */
font-size: 1.25rem; /* = 20px */
/* Fluide : 16px → 32px entre 320px et 1200px */
font-size: clamp(1rem, 2.5vw, 2rem);
/* Grande headline */
font-size: clamp(2rem, 6vw, 5rem);
Disponibilité : Dépend de la police chargée. Si une graisse n'existe pas, le navigateur en simule une autre. Google Fonts permet de choisir les graisses à charger.
font-weight: 400;
/* Semi-bold pour les labels */
font-weight: 600;
/* Bold pour les titres */
font-weight: 700;
/* Ultra pour les grandes headlines */
font-weight: 900;
font-size — recommandé car il s'adapte à toutes les tailles.Recommandations :
• Texte courant :
1.5 à 1.7• Titres :
1.1 à 1.3• Labels courts :
1font-size: 1rem;
line-height: 1.6;
/* Titre serré */
font-size: 3rem;
line-height: 1.15;
/* Bouton (centrage vertical) */
line-height: 1;
Usages SaaS :
•
0.1em : labels, badges, tags (caps)•
-0.02em : titres volumineux pour compenser l'écartement optique•
0.5px : navigation, menus.label {
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.1em;
}
/* Grande headline resserrée */
h1 {
font-size: 5rem;
letter-spacing: -0.03em;
}
Valeurs :
•
uppercase : TOUT EN MAJUSCULES•
lowercase : tout en minuscules•
capitalize : Première Lettre De Chaque Mot•
none : texte tel quel.badge {
text-transform: uppercase;
letter-spacing: 0.08em;
}
/* Titres de section */
.nav-group-title {
text-transform: uppercase;
font-size: 0.65rem;
}
box-shadow mais sans spread ni inset.Effets SaaS :
• Glow néon : ombre floue de la couleur du texte
• Ombres multiples : pour des effets 3D ou de profondeur
.hero-text {
color: var(--cyan);
text-shadow: 0 0 20px rgba(0,210,255,0.5);
}
/* Glow multicolore */
text-shadow:
0 0 10px #00d2ff,
0 0 40px #00d2ff,
0 0 80px rgba(0,210,255,0.3);
text-overflow : Que faire quand le texte dépasse avec
overflow: hidden. ellipsis affiche ....Pattern ellipsis : Les 3 propriétés doivent être combinées.
.card-title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* Ellipsis sur N lignes (WebKit) */
.excerpt {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
Layout & Position
CSSContrôle du flux, du positionnement et du comportement d'affichage.
Valeurs :
•
static : flux normal (défaut)•
relative : décalage par rapport à sa position normale (gardé dans le flux)•
absolute : sort du flux, positionné par rapport au parent positionné•
fixed : fixé dans le viewport (scrolls pas)•
sticky : relatif jusqu'à un seuil, puis fixe.parent { position: relative; }
/* Badge positionné */
.badge {
position: absolute;
top: -8px; right: -8px;
}
/* Topbar collante */
.topbar {
position: sticky;
top: 0;
z-index: 100;
}
Valeurs essentielles :
•
block : pleine largeur, nouvelle ligne•
inline : dans le flux du texte•
inline-block : inline mais avec dimensions•
flex : layout flexbox•
grid : layout grille•
none : masque complètement (supprime de l'espace).hidden { display: none; }
/* Centrer contenu */
.center {
display: flex;
place-items: center;
}
/* Inline avec taille */
.btn-icon {
display: inline-flex;
align-items: center;
gap: 8px;
}
position ≠ static). Valeur plus élevée = au-dessus.Bonnes pratiques : Utilisez des variables CSS pour une gestion systématique. Évitez les
9999 aléatoires — créez une échelle.:root {
--z-base: 1;
--z-dropdown: 10;
--z-sticky: 100;
--z-modal: 1000;
--z-toast: 9999;
}
.modal { z-index: var(--z-modal); }
Valeurs :
•
cover : remplit tout (peut couper) ✅ le plus utilisé•
contain : entier visible (peut laisser des bandes)•
fill : étire pour remplir (déforme)•
none : taille originale.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
object-position: center top;
}
Effets visuels & Animations
CSSTransformations, filtres, animations @keyframes et transitions.
Fonctions :
translateX/Y(), translate(x, y), rotate(deg), scale(), skew(), perspective(), et leurs équivalents 3D..card:hover {
transform: translateY(-4px);
}
/* Spinner rotatif */
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Scale au clic */
.btn:active {
transform: scale(0.96);
}
Fonctions :
blur(), brightness(), contrast(), grayscale(), hue-rotate(), saturate(), invert(), drop-shadow()..logo { filter: grayscale(100%); }
.logo:hover { filter: none; }
/* Glow lumineux */
.icon-glow {
filter: drop-shadow(0 0 8px var(--cyan));
}
/* Image assombrie */
.hero-bg { filter: brightness(0.4); }
Glassmorphism : La combinaison
background: rgba + backdrop-filter: blur crée l'effet verre dépoli..glass-card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(20px) saturate(180%);
border: 1px solid rgba(255,255,255,0.1);
}
/* Topbar floutée */
.topbar {
background: rgba(10,10,18,0.8);
backdrop-filter: blur(10px);
}
Formes :
circle(), ellipse(), polygon(), inset(), path()..avatar { clip-path: circle(50%); }
/* Hexagone */
.hex {
clip-path: polygon(
50% 0%, 100% 25%, 100% 75%,
50% 100%, 0% 75%, 0% 25%);
}
/* Diagonale en bas */
.hero {
clip-path: polygon(0 0, 100% 0, 100% 90%, 0 100%);
}
from, to).animation : Raccourci pour appliquer et configurer l'animation :
nom durée timing délai itérations direction fill-mode play-state.@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to { opacity: 1; transform: none; }
}
/* Appliquer */
.hero h1 {
animation: fadeInUp 0.6s ease forwards;
}
/* Spinner */
@keyframes spin { to { transform: rotate(360deg); } }
.loader { animation: spin 1s linear infinite; }
@keyframes.Syntaxe :
propriété durée timing délai. all anime toutes les propriétés (moins performant)..btn {
background: var(--cyan);
transition: background 0.2s ease,
transform 0.15s ease,
box-shadow 0.2s ease;
}
.btn:hover {
background: var(--cyan-bright);
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0,210,255,0.4);
}
Flexbox
CSSLe modèle de boîte flexible pour l'alignement unidimensionnel. Incontournable pour les navbars, centrage et distributions.
display: flex transforme un élément en conteneur flex. Ses enfants directs deviennent des flex items alignés en ligne par défaut.Axe principal : Horizontal par défaut (row). L'axe croisé est vertical. Tout l'alignement s'effectue par rapport à ces deux axes.
Valeurs clés :
•
flex-start : tassé à gauche (défaut)•
center : centré•
flex-end : tassé à droite•
space-between : espacés, pas de marge extérieure•
space-around : espacés avec demi-marge extérieurejustify-content: center + align-items: center est la façon la plus simple de centrer un élément.Valeurs :
stretch (défaut), center, flex-start, flex-end, baseline.Valeurs :
•
row : horizontal, gauche→droite (défaut)•
row-reverse : horizontal, droite→gauche•
column : vertical, haut→bas•
column-reverse : vertical, bas→hautAstuce : Avec
column, justify-content devient vertical et align-items horizontal.flex-grow flex-shrink flex-basis. flex: 1 = l'item prend tout l'espace disponible.flex-wrap :
wrap autorise le retour à la ligne quand les items débordent. Essentiel pour le responsive.gap : Espacement entre les items (remplace les marges).
CSS Grid
CSSMise en page bidimensionnelle (lignes + colonnes). Parfait pour les layouts de pages, dashboards et grilles de cartes.
Flexbox vs Grid : Flexbox = 1D (une seule direction). Grid = 2D (lignes ET colonnes simultanément). Utilisez les deux ensemble!
1fr 2fr = 33% + 66%.repeat() :
repeat(3, 1fr) = 3 colonnes égales. repeat(auto-fit, minmax(280px, 1fr)) = grille responsive automatique sans media queries!minmax() : Définit une taille min et max pour chaque colonne.
Syntaxe :
grid-column: 1 / 3 = de la ligne 1 à la ligne 3 (2 colonnes). span 2 = s'étale sur 2 colonnes depuis sa position actuelle.Avantage : Très lisible, idéal pour les layouts de pages. Chaque zone est un string,
. = cellule vide.Bases & Syntaxe
JAVASCRIPTLes fondamentaux de JavaScript : variables, types, syntaxe de base.
const : constante — ne peut pas être réassignée. Utilisez
const par défaut et let uniquement si la valeur change. Les tableaux et objets const restent mutables.var : ancien système, portée de fonction. Evitez-le — hoisting, pas de portée de bloc.
${...}. Supportent les retours à la ligne natifs.Avantages : Plus lisible que la concaténation avec
+. Expressions complexes possibles : ${a > b ? a : b}.Variantes :
console.warn() (jaune), console.error() (rouge), console.table() (tableau), console.time()/console.timeEnd() (mesure de performance).Types de Données
JSJavaScript a 8 types : 7 primitifs et le type objet.
Méthodes utiles :
.length, .toUpperCase(), .toLowerCase(), .trim(), .includes(), .startsWith(), .split(), .replace(), .slice().Détails des méthodes :
.length — propriété (pas de parenthèses) : renvoie le nombre de caractères..toUpperCase() — renvoie la chaîne en majuscules..toLowerCase() — renvoie la chaîne en minuscules..trim() — enlève les espaces (et retours ligne) au début et à la fin..includes("x") — renvoie true si la sous-chaîne est trouvée (sensible à la casse)..startsWith("x") — renvoie true si la chaîne commence par ce texte..split(sep) — découpe en tableau selon un séparateur (ex : ",", " ")..replace(a, b) — remplace la première occurrence (ou toutes avec une regex /.../g)..slice(debut, fin) — extrait une partie (fin exclue). Accepte des index négatifs (ex : -1 = dernier caractère).Notes :
includes et startsWith sont sensibles à la casse : "Julien".includes("jul") vaut false.
const greeting = `Bonjour ${nom}!`;
nom.length // 6
nom.toUpperCase() // "JULIEN"
nom.toLowerCase() // "julien"
" texte ".trim() // "texte"
nom.includes("ul") // true
nom.startsWith("Ju") // true
"a,b,c".split(",") // ["a","b","c"]
"un deux trois".split(" ") // ["un","deux","trois"]
"Bonjour Julien".replace("Julien", "Marie") // "Bonjour Marie"
"ha ha ha".replace(/ha/g, "ho") // "ho ho ho"
nom.slice(0, 3) // "Jul"
nom.slice(-1) // "n"
Number) pour les entiers et les décimaux. Précision limitée pour les très grands entiers (utilisez BigInt).Valeurs spéciales :
Infinity, -Infinity, NaN (Not a Number — résultat d'opération invalide).Math :
Math.round(), Math.floor(), Math.ceil(), Math.random(), Math.abs(), Math.max().Détails des méthodes :
Math.round(x) — arrondit à l’entier le plus proche (0.5 arrondit “vers le haut” côté positif : 2.5 → 3).Math.floor(x) — arrondit à l’entier inférieur (vers -Infinity) : 3.9 → 3, -1.2 → -2.Math.ceil(x) — arrondit à l’entier supérieur (vers +Infinity) : 3.1 → 4, -1.2 → -1.Math.random() — renvoie un nombre pseudo-aléatoire dans [0, 1) (0 inclus, 1 exclu).Math.abs(x) — valeur absolue : transforme un nombre en positif (ou 0).Math.max(a, b, ...) — renvoie le plus grand des nombres passés en paramètres (attention : pas directement sur un tableau sans ...).Astuce : nombre aléatoire entier entre
min et max inclus :
Math.floor(Math.random() * (max - min + 1)) + min.
const score = 100;
Number("42") // 42
parseInt("3.7") // 3
parseFloat("3.7") // 3.7
isNaN("abc") // true
Math.round(29.49) // 29
Math.round(29.50) // 30
Math.floor(3.9) // 3
Math.ceil(3.1) // 4
Math.floor(-1.2) // -2
Math.ceil(-1.2) // -1
Math.random() // 0.0 à 0.999...
const de = Math.floor(Math.random() * 6) + 1; // 1 à 6
Math.abs(-10) // 10
Math.max(3, 10, 7) // 10
Math.max(...[3, 10, 7]) // 10
Valeurs false :
false, 0, -0, 0n, "", null, undefined, NaN. Tout le reste est true (même [] et {})!const desactive = false;
// True / False
if ([]) { /* ✅ true! */ }
if (0) { /* ❌ false */ }
if ("") { /* ❌ false */ }
Boolean(0) // false
Boolean("a") // true
Boolean(null) // false
undefined : Variable déclarée mais pas encore assignée. Retourné par les fonctions sans
return, les propriétés inexistantes.Comparaison :
null == undefined est true, mais null === undefined est false.let user; // undefined
const obj = {};
obj.name; // undefined
// null : intentionnellement vide
let token = null; // pas encore connecté
// Guard clause
if (user === null || user === undefined) {
// Ou : if (user == null)
}
Type :
typeof [] retourne "object". Pour tester si c'est un tableau : Array.isArray([]) → true.users[0] // "Alice"
users.length // 3
users.at(-1) // "Julien" (dernier)
// Types mixtes (déconseillé mais possible)
const mixed = [1, "hello", true, null];
Array.isArray([]) // true ✅
Attention : Les objets sont passés par référence — assigner un objet à une variable ne crée pas une copie!
nom: "Julien",
age: 28,
actif: true,
saluer() { return `Hi ${this.nom}`; }
};
user.nom // "Julien"
user.saluer() // "Hi Julien"
Conditions
JAVASCRIPTLes structures conditionnelles contrôlent le flux d'exécution.
else if, terminer par else.Valeurs falsy :
false, 0, "", null, undefined, NaN. Tout le reste est truthy — même [] et {}!condition ? siVrai : siFaux. Retourne une valeur selon une condition.Usage : Assignations conditionnelles, affichage conditionnel. Evitez d'imbriquer — lisibilité réduite. Pour des cas complexes, préférez
if/else.Asynchrone
JSPromises et async/await pour gérer les opérations non-bloquantes.
clearTimeout(id).setInterval : Exécute une fonction à intervalles répétitifs. Toujours nettoyer avec
clearInterval(id) pour éviter les fuites mémoire.const id = setTimeout(() => {
console.log("2 secondes écoulées!");
}, 2000);
// Annuler si besoin
clearTimeout(id);
// Répéter toutes les secondes
const timer = setInterval(() => {
updateClock();
}, 1000);
// Arrêter (ex: au démontage du composant)
clearInterval(timer);
pending (en attente), fulfilled (résolue), rejected (rejetée).En pratique : Vous créez rarement des Promises manuellement — elles sont retournées par les API (
fetch, fs.readFile...). Vous les consommez avec .then/.catch ou async/await.const p = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) resolve("✅ Succès");
else reject(new Error("❌ Echec"));
}, 1000);
});
p.then(result => console.log(result))
.catch(err => console.error(err));
async transforme une fonction en Promise, await attend la résolution d'une Promise.Règle :
await ne peut être utilisé qu'à l'intérieur d'une fonction async.async function loadUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error("Erreur API");
const data = await res.json();
return data;
} catch (err) {
console.error("Echec:", err.message);
}
}
Important : fetch ne rejette pas pour les erreurs HTTP (404, 500) — il faut vérifier
response.ok. Il rejette uniquement pour les erreurs réseau.const data = await (await fetch("/api/users")).json();
// POST avec body JSON
const res = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ nom: "Julien" })
});
if (!res.ok) throw new Error(res.statusText);
const user = await res.json();
Promise.allSettled : Attend toutes les promises, retourne toutes les résolutions et rejections. Utile pour les cas où on veut tous les résultats.
const [users, posts, stats] = await Promise.all([
fetch("/api/users").then(r => r.json()),
fetch("/api/posts").then(r => r.json()),
fetch("/api/stats").then(r => r.json()),
]);
// allSettled : recupérer même les erreurs
const results = await Promise.allSettled([p1, p2, p3]);
results.forEach(r => {
if (r.status === "fulfilled") use(r.value);
});
ES6+ JavaScript Moderne
JSLes fonctionnalités modernes de JavaScript indispensables en SaaS.
Usage avancé : Très utilisé dans les paramètres de fonctions pour documenter les arguments attendus.
const { nom, age, ville = "Paris" } = user;
// Renommer : const { nom: username } = user;
// Tableau
const [first, second, ...rest] = [1, 2, 3, 4];
// Dans les paramètres de fonction
function greet({ nom, role = "user" }) {
return `${nom} (${role})`;
}
Rest (...) : Collecte les éléments restants dans un tableau (params de fonction) ou objet.
Distinction : Même syntaxe, direction opposée — spread étale, rest collecte.
const arr2 = [...arr1, 4, 5];
const updated = { ...user, role: "admin" };
Math.max(...numbers);
// REST — collecter
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
const { id, ...rest } = user; // omit id
Set : Collection de valeurs uniques. Parfait pour dédupliquer des données.
const cache = new Map();
cache.set("user:1", { nom: "Julien" });
cache.get("user:1"); // { nom: "Julien" }
cache.has("user:1"); // true
cache.size; // 1
// Set — valeurs uniques
const tags = new Set(["js", "ts", "js"]);
// Set { "js", "ts" } — doublon supprimé
// Dédupliquer un tableau
const unique = [...new Set(arr)];
TypeError si une valeur intermédiaire est null ou undefined. Retourne undefined au lieu de planter.Fonctionne aussi pour : méthodes (
obj?.method()), tableaux (arr?.[0]), fonctions (fn?.()).const city = user && user.address && user.address.city;
// Avec ?. — sûr et concis
const city = user?.address?.city;
// Méthode optionnelle
user?.getPermissions?.();
// Avec valeur par défaut (nullish)
const avatar = user?.profile?.avatar ?? "default.png";
Export par défaut : Un seul par fichier. Peut être importé avec n'importe quel nom.
import type : En TypeScript, importer uniquement des types (supprimé à la compilation).
export const PI = 3.14159;
export function formatDate(d) { ... }
export { helperA, helperB };
// UserService.js — export par défaut
export default class UserService { ... }
// Importer
import { PI, formatDate } from "./utils.js";
import UserService from "./UserService.js";
import * as Utils from "./utils.js";
Méthodes des Tableaux
JAVASCRIPTLes méthodes fonctionnelles des arrays sont au cœur du développement JS moderne : transformer, filtrer et accûmuler des données.
Cas d'usage SaaS : Transformer des données API en composants UI, formater des prix, convertir des IDs en objets, générer du HTML dynamique.
true. La longueur peut être inférieure.Chaînage :
.filter().map() est un pattern très courant : d'abord filtrer, ensuite transformer.Paramètres :
reduce(callback, valeurInitiale). Le callback reçoit (accumulateur, valeurCourante).undefined. Très utile pour trouver un objet par ID..some() : Retourne
true si au moins un élément satisfait la condition. Court-circuite dès le premier match..every() : Retourne
true si tous les éléments satisfont la condition..slice(debut, fin) : Extrait sans modifier l'original.
slice(-3) = les 3 derniers..splice(index, n, ...items) : Modifie l'original en supprimant/insérant des éléments.
Manipulation du DOM
JAVASCRIPTInteragir avec les éléments HTML depuis JavaScript : sélection, événements, modification du contenu et des classes.
querySelectorAll retourne tous les éléments sous forme de NodeList.Syntaxe : Identique aux sélecteurs CSS :
#id, .classe, tag, [attr], etc.Evénements courants :
click, input, change, submit, keydown, keyup, scroll, resize, DOMContentLoaded, mouseover, mouseout.event.preventDefault() : Empêche le comportement par défaut (ex: soumission de formulaire).
•
.add("classe") : ajouter•
.remove("classe") : supprimer•
.toggle("classe") : ajouter si absente, supprimer si présente•
.contains("classe") : vérifier la présence (retourne booléen)•
.replace("ancienne", "nouvelle") : remplacertextContent : Définit le texte brut sans interpréter le HTML. Plus sécurisé pour le contenu utilisateur.
insertAdjacentHTML : Insère sans écraser le contenu existant.
Closures & Scope avancé
JS AvancéComprendre les closures, le lexical scope, le hoisting et le mot-clé this — fondamentaux pour écrire du JavaScript professionnel.
Comment ça marche : Quand une fonction est créée, elle capture une référence vers son environnement lexical (les variables disponibles au moment de sa création).
Cas d'usage :
• Encapsulation de données (modules)
• Fonctions factory (créer des variantes)
• Callbacks et event handlers
• Mémoïsation / cache
Piège courant : Les closures capturent la référence à la variable, pas sa valeur au moment de la capture.
// Closure basique — compteur privé
function createCounter(initial = 0) {
let count = initial; // Variable "enfermée"
return {
increment: () => ++count,
decrement: () => --count,
getValue: () => count,
reset: () => { count = initial; }
};
}
const counter = createCounter(10);
counter.increment(); // 11
counter.increment(); // 12
counter.getValue(); // 12
// count est inaccessible de l'extérieur ✅
// Factory function — créer des variantes
function createMultiplier(factor) {
return (number) => number * factor;
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
double(5); // 10
triple(5); // 15
// ⚠️ Piège classique avec var dans une boucle
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Affiche : 3, 3, 3 (pas 0, 1, 2)
// ✅ Solution avec let (block scope)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// Affiche : 0, 1, 2Hoisting : JS "remonte" les déclarations en haut de leur portée lors de la compilation :
•
var → déclaration remontée, valeur = undefined•
let/const → remontées mais dans la Temporal Dead Zone (erreur si accédées avant déclaration)•
function déclaration → entièrement remontée (corps inclus)•
function expression → comme var/letPortées : Globale → Fonction → Bloc (let/const)
// ─── Lexical Scope (scope chain) ───
const app = 'NovaDash';
function outer() {
const version = '2.0';
function inner() {
const module = 'crypto';
// Accès : module ✅, version ✅, app ✅
console.log(`${app} v${version} — ${module}`);
}
inner();
// Accès : version ✅, app ✅, module ❌
}
// ─── Hoisting — var vs let ───
console.log(a); // undefined (var est remonté)
console.log(b); // ❌ ReferenceError (TDZ)
var a = 1;
let b = 2;
// ─── Hoisting — fonctions ───
// Déclaration → entièrement remontée
greet(); // ✅ "Hello" — fonctionne !
function greet() { console.log('Hello'); }
// Expression → pas remontée
sayBye(); // ❌ TypeError
const sayBye = () => console.log('Bye');
// ─── Block scope ───
if (true) {
var x = 1; // Fuit hors du bloc
let y = 2; // Reste dans le bloc
const z = 3; // Reste dans le bloc
}
console.log(x); // 1
console.log(y); // ❌ ReferenceErrorRègles de résolution :
1.
new → this = nouvel objet2.
call/apply/bind → this = objet passé3. Méthode d'objet → this = l'objet
4. Fonction libre → this = window (ou undefined en strict)
5. Arrow function → this = celui du scope parent (lexical)
call vs apply vs bind :
•
call(obj, arg1, arg2) → appel immédiat, args séparés•
apply(obj, [args]) → appel immédiat, args en tableau•
bind(obj) → retourne une nouvelle fonction (pas d'appel)
// ─── this selon le contexte ───
const user = {
name: 'Julien',
greet() {
console.log(this.name); // 'Julien'
},
greetLater() {
// ❌ this perdu dans setTimeout
setTimeout(function() {
console.log(this.name); // undefined
}, 100);
// ✅ Arrow function → this lexical
setTimeout(() => {
console.log(this.name); // 'Julien'
}, 100);
}
};
// ─── call / apply / bind ───
function introduce(greeting, emoji) {
console.log(`${greeting} ${this.name} ${emoji}`);
}
const dev = { name: 'Julien' };
// call — args séparés
introduce.call(dev, 'Salut', '👋');
// apply — args en tableau
introduce.apply(dev, ['Salut', '👋']);
// bind — retourne une nouvelle fonction
const boundIntro = introduce.bind(dev);
boundIntro('Hello', '🚀'); // Hello Julien 🚀
// ─── Cas pratique : emprunt de méthode ───
const logger = {
prefix: '[LOG]',
log(msg) { console.log(`${this.prefix} ${msg}`); }
};
const errorLogger = { prefix: '[ERROR]' };
logger.log.call(errorLogger, 'Crash !');
// [ERROR] Crash !Module Pattern : Combine IIFE + closures pour créer des modules avec état privé et API publique. C'était LE pattern avant ES6 modules.
Revealing Module Pattern : Variante plus lisible qui déclare tout en privé puis "révèle" les méthodes publiques dans le return.
Aujourd'hui : Préférer les ES Modules (
import/export), mais le module pattern reste utile pour comprendre les closures et encapsuler du code dans un seul fichier.
// ─── IIFE — Portée isolée ───
(function() {
const secret = 'abc123';
console.log('Initialisé');
// secret n'existe pas dehors
})();
// Avec paramètres
(function(win, doc) {
// Alias locaux pour window et document
})(window, document);
// ─── Module Pattern ───
const CryptoWallet = (function() {
// 🔒 État privé
let balance = 0;
const transactions = [];
// Fonction privée
function log(type, amount) {
transactions.push({ type, amount, date: new Date() });
}
// 🔓 API publique
return {
deposit(amount) {
balance += amount;
log('deposit', amount);
},
withdraw(amount) {
if (amount > balance) throw new Error('Fonds insuffisants');
balance -= amount;
log('withdraw', amount);
},
getBalance: () => balance,
getHistory: () => [...transactions] // copie
};
})();
CryptoWallet.deposit(1000);
CryptoWallet.withdraw(250);
CryptoWallet.getBalance(); // 750
CryptoWallet.getHistory(); // [{...}, {...}]
// CryptoWallet.balance // undefined 🔒
// CryptoWallet.transactions // undefined 🔒Prototypes & Classes avancées
JS AvancéLe système d'héritage prototype de JavaScript et les classes ES6+ qui l'encapsulent. Comprendre les prototypes est essentiel pour maîtriser le langage.
[[Prototype]] vers un autre objet. Quand on accède à une propriété absente, JS remonte la chaîne de prototypes jusqu'à null.__proto__ vs prototype :
•
__proto__ → le prototype de l'instance (utiliser Object.getPrototypeOf())•
.prototype → propriété des fonctions constructeurs, assignée aux instances créées avec newChaîne :
instance → Constructor.prototype → Object.prototype → nullPerformance : Les méthodes sur le prototype sont partagées entre toutes les instances (économie mémoire), contrairement aux méthodes définies dans le constructeur.
// ─── Constructeur + prototype ───
function Crypto(name, symbol) {
this.name = name; // Propre à chaque instance
this.symbol = symbol;
}
// Méthode partagée via prototype
Crypto.prototype.display = function() {
return `${this.name} (${this.symbol})`;
};
const btc = new Crypto('Bitcoin', 'BTC');
const eth = new Crypto('Ethereum', 'ETH');
btc.display(); // "Bitcoin (BTC)"
eth.display(); // "Ethereum (ETH)"
// Même fonction en mémoire
btc.display === eth.display; // true ✅
// ─── Chaîne de prototypes ───
console.log(
Object.getPrototypeOf(btc) === Crypto.prototype, // true
Object.getPrototypeOf(Crypto.prototype) === Object.prototype, // true
Object.getPrototypeOf(Object.prototype) === null // true — fin
);
// ─── Vérifications ───
btc instanceof Crypto; // true
btc.hasOwnProperty('name'); // true (propre)
btc.hasOwnProperty('display'); // false (hérité)
'display' in btc; // true (remonte la chaîne)
// ─── Object.create — héritage sans new ───
const baseConfig = { theme: 'dark', lang: 'fr' };
const userConfig = Object.create(baseConfig);
userConfig.fontSize = 16;
userConfig.theme; // 'dark' (hérité)
userConfig.fontSize; // 16 (propre)Éléments :
•
constructor() → initialisation• Méthodes → automatiquement sur le prototype
•
static → méthode de classe (pas d'instance)•
get/set → propriétés calculées•
extends → héritage•
super() → appel du parentChamps privés (ES2022) : Préfixe
# pour les propriétés/méthodes vraiment privées (pas juste une convention).
class Portfolio {
// Champ privé (ES2022)
#holdings = new Map();
#owner;
constructor(owner) {
this.#owner = owner;
}
// Méthode publique
buy(symbol, amount) {
const current = this.#holdings.get(symbol) || 0;
this.#holdings.set(symbol, current + amount);
}
// Getter
get totalAssets() {
return this.#holdings.size;
}
// Méthode statique
static compare(a, b) {
return a.totalAssets - b.totalAssets;
}
// Méthode privée
#validate(amount) {
if (amount <= 0) throw new Error('Montant invalide');
}
}
// ─── Héritage avec extends ───
class PremiumPortfolio extends Portfolio {
#leverage;
constructor(owner, leverage = 2) {
super(owner); // Appel du constructeur parent
this.#leverage = leverage;
}
// Override de méthode
buy(symbol, amount) {
super.buy(symbol, amount * this.#leverage);
}
}
const p = new PremiumPortfolio('Julien', 3);
p.buy('BTC', 1); // Achète 3 BTC (levier x3)
p.totalAssets; // 1
// p.#holdings // ❌ SyntaxError — privéIterator Protocol : Un objet est itérable s'il implémente
[Symbol.iterator]() qui retourne un objet avec une méthode next(). Permet l'utilisation de for...of, spread, destructuring.Generators : Fonctions avec
function* qui peuvent suspendre leur exécution avec yield. Retournent un iterator. Parfait pour les séquences paresseuses, la pagination, et les flux asynchrones.
// ─── Symbol ───
const id = Symbol('userId');
const user = { [id]: 42, name: 'Julien' };
user[id]; // 42
Object.keys(user); // ['name'] — Symbol non listé
// Symbols globaux partagés
const KEY = Symbol.for('app.key');
Symbol.for('app.key') === KEY; // true
// ─── Iterator personnalisé ───
class CryptoList {
#coins;
constructor(...coins) { this.#coins = coins; }
[Symbol.iterator]() {
let i = 0;
const coins = this.#coins;
return {
next() {
return i < coins.length
? { value: coins[i++], done: false }
: { done: true };
}
};
}
}
const list = new CryptoList('BTC', 'ETH', 'SOL');
for (const coin of list) console.log(coin);
const arr = [...list]; // ['BTC', 'ETH', 'SOL']
// ─── Generator ───
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
fib.next().value; // 0
fib.next().value; // 1
fib.next().value; // 1
fib.next().value; // 2
// Generator pour pagination API
async function* fetchPages(url) {
let page = 1;
while (true) {
const res = await fetch(`${url}?page=${page}`);
const data = await res.json();
if (data.length === 0) return;
yield data;
page++;
}
}Traps courants :
•
get(target, prop) → lecture de propriété•
set(target, prop, value) → écriture•
has(target, prop) → opérateur in•
deleteProperty(target, prop) → delete•
apply(target, thisArg, args) → appel de fonctionReflect : API miroir qui fournit les comportements par défaut des traps. Utilisé dans les handlers pour déléguer au comportement original.
// ─── Proxy — Validation automatique ───
function createValidated(schema) {
return new Proxy({}, {
set(target, prop, value) {
if (schema[prop]) {
const { type, min, max } = schema[prop];
if (typeof value !== type)
throw new TypeError(`${prop} doit être ${type}`);
if (min !== undefined && value < min)
throw new RangeError(`${prop} min: ${min}`);
if (max !== undefined && value > max)
throw new RangeError(`${prop} max: ${max}`);
}
return Reflect.set(target, prop, value);
},
get(target, prop) {
if (!(prop in target))
console.warn(`Propriété "${prop}" non définie`);
return Reflect.get(target, prop);
}
});
}
const trade = createValidated({
amount: { type: 'number', min: 0 },
symbol: { type: 'string' },
leverage: { type: 'number', min: 1, max: 100 }
});
trade.symbol = 'BTC'; // ✅
trade.amount = 1000; // ✅
trade.leverage = 50; // ✅
// trade.amount = -5; // ❌ RangeError
// trade.symbol = 123; // ❌ TypeError
// ─── Proxy — Observable (style Vue.js) ───
function reactive(obj, onChange) {
return new Proxy(obj, {
set(target, prop, value) {
const old = target[prop];
const result = Reflect.set(target, prop, value);
if (old !== value) onChange(prop, value, old);
return result;
}
});
}
const state = reactive({ count: 0 }, (prop, val) => {
console.log(`${prop} changed to ${val}`);
});
state.count = 5; // "count changed to 5"Event Loop & Concurrence
JS AvancéComment JavaScript gère l'asynchrone avec un seul thread. Comprendre l'event loop, les microtasks et macrotasks, et les patterns de concurrence avancés.
Ordre d'exécution :
1. Call Stack — code synchrone en cours
2. Microtask Queue — Promises (.then), queueMicrotask, MutationObserver
3. Macrotask Queue — setTimeout, setInterval, I/O, events DOM
Règle clé : Les microtasks sont TOUJOURS exécutées avant les macrotasks. Après chaque macrotask, TOUTES les microtasks en attente sont vidées.
requestAnimationFrame : Exécuté avant le prochain repaint du navigateur (~60fps). Idéal pour les animations.
// ─── Quiz : quel ordre d'affichage ? ───
console.log('1 — Sync');
setTimeout(() => console.log('2 — Timeout'), 0);
Promise.resolve().then(() => console.log('3 — Promise'));
queueMicrotask(() => console.log('4 — Microtask'));
console.log('5 — Sync');
// Résultat :
// 1 — Sync (call stack)
// 5 — Sync (call stack)
// 3 — Promise (microtask)
// 4 — Microtask (microtask)
// 2 — Timeout (macrotask)
// ─── Pourquoi c'est important ───
// Les microtasks peuvent bloquer le rendu !
Promise.resolve().then(function loop() {
// ⚠️ Boucle infinie de microtasks
// Le navigateur ne peut JAMAIS peindre
Promise.resolve().then(loop);
});
// ─── requestAnimationFrame ───
function animate(element) {
let start = null;
function step(timestamp) {
if (!start) start = timestamp;
const progress = (timestamp - start) / 1000;
element.style.transform =
`translateX(${progress * 200}px)`;
if (progress < 2) {
requestAnimationFrame(step); // ~60fps
}
}
requestAnimationFrame(step);
}•
Promise.all([...]) → attend TOUTES. Échoue si UNE échoue•
Promise.allSettled([...]) → attend TOUTES, jamais d'échec. Retourne le statut de chaque promise•
Promise.race([...]) → retourne la PREMIÈRE (résolue OU rejetée)•
Promise.any([...]) → retourne la PREMIÈRE résolue. Échoue seulement si TOUTES échouentCas d'usage SaaS :
•
all → charger plusieurs API en parallèle•
allSettled → dashboard multi-sources (certaines peuvent échouer)•
race → timeout sur une requête•
any → fallback entre plusieurs serveurs
// ─── Promise.all — tout ou rien ───
const [user, trades, prices] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/trades').then(r => r.json()),
fetch('/api/prices').then(r => r.json())
]);
// Si UNE échoue → tout échoue
// ─── Promise.allSettled — résultats partiels ───
const results = await Promise.allSettled([
fetch('/api/binance'),
fetch('/api/kraken'),
fetch('/api/coinbase')
]);
results.forEach(r => {
if (r.status === 'fulfilled')
console.log('OK:', r.value);
else
console.log('Erreur:', r.reason);
});
// ─── Promise.race — timeout pattern ───
function fetchWithTimeout(url, ms = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
)
]);
}
// ─── Promise.any — premier succès ───
const fastest = await Promise.any([
fetch('https://api1.example.com/price'),
fetch('https://api2.example.com/price'),
fetch('https://api3.example.com/price')
]);
// Retourne la première réponse réussie
// ─── Chaînage avancé ───
const portfolio = await fetch('/api/portfolio')
.then(r => { if (!r.ok) throw r; return r.json(); })
.then(data => data.filter(c => c.value > 100))
.then(filtered => filtered.sort((a, b) => b.value - a.value))
.catch(err => { console.error(err); return []; })
.finally(() => console.log('Requête terminée'));Web Workers : Exécutent du JS dans un thread séparé. Communication par messages (
postMessage / onmessage). Pas d'accès au DOM.Types :
• Dedicated Worker — un seul script propriétaire
• Shared Worker — partagé entre plusieurs onglets
• Service Worker — proxy réseau (cache, offline, push notifications)
Transferables : Pour les gros tableaux, utilisez
postMessage(data, [buffer]) pour transférer la mémoire (pas copier) — beaucoup plus rapide.
// ─── Main thread (app.js) ───
const worker = new Worker('analyzer.js');
// Envoyer des données au worker
worker.postMessage({
type: 'ANALYZE',
prices: [95000, 95200, 94800, 95500, 96000]
});
// Recevoir les résultats
worker.onmessage = (event) => {
const { sma, rsi, signal } = event.data;
document.getElementById('signal').textContent = signal;
};
worker.onerror = (err) => console.error('Worker:', err);
// Terminer le worker
// worker.terminate();
// ─── Worker (analyzer.js) ───
self.onmessage = function(event) {
const { type, prices } = event.data;
if (type === 'ANALYZE') {
// Calculs lourds sans bloquer l'UI
const sma = prices.reduce((a, b) => a + b)
/ prices.length;
const rsi = calculateRSI(prices);
const signal = rsi > 70 ? 'SELL' :
rsi < 30 ? 'BUY' : 'HOLD';
// Retourner les résultats
self.postMessage({ sma, rsi, signal });
}
};
function calculateRSI(prices) {
// ... calcul intensif du RSI
return 65;
}
// ─── Inline Worker (sans fichier séparé) ───
const blob = new Blob([`
self.onmessage = (e) => {
const result = e.data * 2;
self.postMessage(result);
};
`], { type: 'application/javascript' });
const inlineWorker = new Worker(URL.createObjectURL(blob));Debounce : Attend que l'utilisateur arrête de taper pendant X ms avant d'exécuter. Idéal pour les recherches.
Throttle : Exécute au maximum une fois toutes les X ms. Idéal pour le scroll, le resize.
Retry avec backoff : Relance une requête échouée avec un délai croissant (exponentiel). Standard pour les API instables.
// ─── AbortController — annuler des requêtes ───
let controller = null;
async function searchCrypto(query) {
// Annuler la requête précédente
if (controller) controller.abort();
controller = new AbortController();
try {
const res = await fetch(`/api/search?q=${query}`, {
signal: controller.signal
});
return await res.json();
} catch (err) {
if (err.name === 'AbortError') return; // Ignoré
throw err;
}
}
// ─── Debounce ───
function debounce(fn, delay = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
const debouncedSearch = debounce(searchCrypto, 400);
input.addEventListener('input', e => {
debouncedSearch(e.target.value);
});
// ─── Throttle ───
function throttle(fn, limit = 100) {
let waiting = false;
return (...args) => {
if (waiting) return;
fn(...args);
waiting = true;
setTimeout(() => { waiting = false; }, limit);
};
}
// ─── Retry avec exponential backoff ───
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url);
if (res.ok) return await res.json();
throw new Error(`HTTP ${res.status}`);
} catch (err) {
if (i === retries - 1) throw err;
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(r => setTimeout(r, delay));
}
}
}Design Patterns
JS AvancéPatterns architecturaux essentiels pour structurer des applications JavaScript maintenables et scalables.
Factory : Crée des objets sans exposer la logique de création. Le code appelant ne sait pas quelle classe concrète est instanciée — il demande un type et reçoit l'objet.
Quand utiliser :
• Singleton → config, logger, connexion API, cache
• Factory → création d'éléments UI, parsers, notifications
// ─── Singleton avec classe ───
class ApiClient {
static #instance = null;
#baseUrl;
#token;
constructor(baseUrl) {
if (ApiClient.#instance) return ApiClient.#instance;
this.#baseUrl = baseUrl;
this.#token = null;
ApiClient.#instance = this;
}
setToken(token) { this.#token = token; }
async get(path) {
return fetch(`${this.#baseUrl}${path}`, {
headers: { Authorization: `Bearer ${this.#token}` }
}).then(r => r.json());
}
}
const api1 = new ApiClient('https://api.novadash.fr');
const api2 = new ApiClient('https://other.url');
api1 === api2; // true — même instance
// ─── Singleton avec module ES6 (plus simple) ───
// api-client.js
// export default new ApiClient('https://...');
// → Même instance partout grâce au cache du module
// ─── Factory Pattern ───
class Notification {
static create(type, message) {
switch (type) {
case 'success': return new SuccessNotif(message);
case 'error': return new ErrorNotif(message);
case 'warning': return new WarningNotif(message);
default: throw new Error(`Type inconnu: ${type}`);
}
}
}
class SuccessNotif {
constructor(msg) { this.msg = msg; this.color = '#0f0'; }
show() { /* ... */ }
}
class ErrorNotif {
constructor(msg) { this.msg = msg; this.color = '#f00'; }
show() { /* ... */ }
}
const n = Notification.create('success', 'Trade exécuté');
n.show();Pub/Sub (EventEmitter) : Version découplée — les publishers émettent des événements sur un bus central, les subscribers écoutent les événements qui les intéressent. Aucune connaissance directe entre émetteur et récepteur.
Cas d'usage :
• Observer → binding UI, formulaires réactifs
• Pub/Sub → communication entre modules indépendants, microservices frontend, logging centralisé
// ─── Observer Pattern ───
class Observable {
#observers = new Set();
subscribe(fn) {
this.#observers.add(fn);
return () => this.#observers.delete(fn); // unsubscribe
}
notify(data) {
this.#observers.forEach(fn => fn(data));
}
}
// Utilisation
const priceStream = new Observable();
const unsub = priceStream.subscribe(price => {
document.getElementById('btc').textContent = price;
});
priceStream.subscribe(price => {
if (price > 100000) alert('BTC > 100K !');
});
priceStream.notify(95000); // Tous les observers notifiés
unsub(); // Désabonner le premier observer
// ─── EventEmitter (Pub/Sub) ───
class EventBus {
#events = new Map();
on(event, handler) {
if (!this.#events.has(event))
this.#events.set(event, new Set());
this.#events.get(event).add(handler);
return () => this.#events.get(event).delete(handler);
}
emit(event, ...args) {
this.#events.get(event)?.forEach(fn => fn(...args));
}
once(event, handler) {
const unsub = this.on(event, (...args) => {
handler(...args);
unsub();
});
}
}
// Bus global de l'app
const bus = new EventBus();
// Module A — émet
bus.emit('trade:executed', { symbol: 'BTC', amount: 0.5 });
// Module B — écoute (ne connaît pas A)
bus.on('trade:executed', (trade) => {
console.log(`Trade: ${trade.symbol}`);
});if/else ou switch.Command : Encapsule une action en objet, permettant de la stocker, passer en paramètre, annuler (
undo), ou rejouer (redo).En JS idiomatique : Ces patterns se simplifient souvent en objets littéraux + fonctions de premier ordre (pas besoin de classes complexes comme en Java).
// ─── Strategy — Algorithmes interchangeables ───
const sortStrategies = {
price: (a, b) => b.price - a.price,
name: (a, b) => a.name.localeCompare(b.name),
volume: (a, b) => b.volume - a.volume,
change: (a, b) => b.change24h - a.change24h
};
function sortCryptos(cryptos, strategy = 'price') {
return [...cryptos].sort(sortStrategies[strategy]);
}
const coins = [
{ name: 'Bitcoin', price: 95000, volume: 50e9, change24h: 2.1 },
{ name: 'Ethereum', price: 3200, volume: 20e9, change24h: -1.3 },
];
sortCryptos(coins, 'price'); // Par prix
sortCryptos(coins, 'name'); // Alphabétique
sortCryptos(coins, 'change'); // Par variation
// ─── Command — Undo/Redo ───
class CommandManager {
#history = [];
#position = -1;
execute(command) {
// Supprimer le futur si on a fait undo
this.#history.splice(this.#position + 1);
command.execute();
this.#history.push(command);
this.#position++;
}
undo() {
if (this.#position < 0) return;
this.#history[this.#position].undo();
this.#position--;
}
redo() {
if (this.#position >= this.#history.length - 1) return;
this.#position++;
this.#history[this.#position].execute();
}
}
// Commande concrète
const addTrade = (portfolio, trade) => ({
execute: () => portfolio.push(trade),
undo: () => portfolio.pop()
});
const mgr = new CommandManager();
const portfolio = [];
mgr.execute(addTrade(portfolio, { symbol: 'BTC' }));
mgr.execute(addTrade(portfolio, { symbol: 'ETH' }));
mgr.undo(); // Retire ETH
mgr.redo(); // Remet ETHCurrying : Transforme une fonction de N arguments en N fonctions d'un argument. Permet la partial application — pré-remplir certains paramètres pour créer des variantes spécialisées.
Composition : Combiner des fonctions simples pour construire des transformations complexes.
pipe(f, g, h)(x) = h(g(f(x))).
// ─── Memoization ───
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
const expensiveCalc = memoize((prices) => {
console.log('Calcul RSI...');
return prices.reduce((a, b) => a + b) / prices.length;
});
expensiveCalc([95, 96, 94]); // "Calcul RSI..." → 95
expensiveCalc([95, 96, 94]); // Cache hit — pas de recalcul
// ─── Currying ───
const curry = (fn) => {
const arity = fn.length;
return function curried(...args) {
if (args.length >= arity) return fn(...args);
return (...more) => curried(...args, ...more);
};
};
const formatPrice = curry((currency, decimals, amount) =>
`${amount.toFixed(decimals)} ${currency}`
);
// Créer des variantes spécialisées
const formatUSD = formatPrice('$', 2);
const formatBTC = formatPrice('BTC', 8);
formatUSD(95000); // "95000.00 $"
formatBTC(0.00045); // "0.00045000 BTC"
// ─── Composition (pipe) ───
const pipe = (...fns) => (x) =>
fns.reduce((acc, fn) => fn(acc), x);
const processPrice = pipe(
(p) => p * 1.2, // Ajouter 20% de marge
(p) => Math.round(p), // Arrondir
(p) => formatUSD(p) // Formater
);
processPrice(100); // "120.00 $"Error. Permet de différencier les types d'erreurs dans les catch et de transporter des données contextuelles (code HTTP, champ invalide...).Gestion globale :
•
window.onerror → erreurs non capturées•
window.onunhandledrejection → Promises non capturées•
try/catch → erreurs synchrones et async/awaitPattern Result : Au lieu de throw, retourner
{ ok, data, error }. Inspiré de Rust/Go — rend la gestion d'erreur explicite sans exceptions.
// ─── Erreurs personnalisées ───
class AppError extends Error {
constructor(message, code, details = {}) {
super(message);
this.name = 'AppError';
this.code = code;
this.details = details;
this.timestamp = new Date().toISOString();
}
}
class ValidationError extends AppError {
constructor(field, message) {
super(message, 'VALIDATION_ERROR', { field });
this.name = 'ValidationError';
}
}
class ApiError extends AppError {
constructor(status, message) {
super(message, 'API_ERROR', { status });
this.name = 'ApiError';
}
}
// Utilisation
async function createTrade(data) {
if (!data.symbol)
throw new ValidationError('symbol', 'Symbole requis');
const res = await fetch('/api/trade', { method: 'POST' });
if (!res.ok)
throw new ApiError(res.status, 'Trade échoué');
return res.json();
}
try {
await createTrade({});
} catch (err) {
if (err instanceof ValidationError) {
showFieldError(err.details.field, err.message);
} else if (err instanceof ApiError) {
showToast(`Erreur serveur (${err.details.status})`);
} else {
throw err; // Erreur inattendue → remonter
}
}
// ─── Pattern Result (sans exceptions) ───
async function safeFetch(url) {
try {
const res = await fetch(url);
const data = await res.json();
return { ok: true, data };
} catch (error) {
return { ok: false, error };
}
}
const result = await safeFetch('/api/price/BTC');
if (result.ok) console.log(result.data);
else console.error(result.error);
// ─── Gestion globale ───
window.addEventListener('unhandledrejection', (e) => {
console.error('Promise non gérée:', e.reason);
// Envoyer au service de monitoring
});Web APIs avancées
JS AvancéAPIs navigateur puissantes pour le stockage, les communications temps réel, les notifications et l'observation du DOM.
sessionStorage : Idem mais effacé à la fermeture de l'onglet. Utile pour les données temporaires de session.
IndexedDB : Base de données objet côté client. Asynchrone, transactionnelle, supporte les index et les requêtes. Pas de limite de taille pratique. Pour les apps offline, le cache de données volumineuses.
Cookies vs Storage : Les cookies sont envoyés à chaque requête HTTP (overhead réseau). localStorage/sessionStorage ne sont jamais envoyés au serveur.
// ─── localStorage ───
// Stocker (toujours en string)
localStorage.setItem('theme', 'dark');
localStorage.setItem('user', JSON.stringify({
name: 'Julien', plan: 'premium'
}));
// Lire
const theme = localStorage.getItem('theme');
const user = JSON.parse(localStorage.getItem('user'));
// Supprimer
localStorage.removeItem('theme');
localStorage.clear(); // Tout supprimer
// Écouter les changements (entre onglets !)
window.addEventListener('storage', (e) => {
console.log(`${e.key}: ${e.oldValue} → ${e.newValue}`);
});
// ─── Wrapper typé avec expiration ───
const store = {
set(key, value, ttlMinutes = null) {
const item = { value, created: Date.now() };
if (ttlMinutes) item.expires = Date.now() + ttlMinutes * 60000;
localStorage.setItem(key, JSON.stringify(item));
},
get(key) {
const raw = localStorage.getItem(key);
if (!raw) return null;
const item = JSON.parse(raw);
if (item.expires && Date.now() > item.expires) {
localStorage.removeItem(key);
return null; // Expiré
}
return item.value;
}
};
store.set('prices', { BTC: 95000 }, 5); // Expire dans 5min
store.get('prices'); // { BTC: 95000 } ou null
// ─── IndexedDB (simplifié) ───
async function openDB(name, version = 1) {
return new Promise((resolve, reject) => {
const req = indexedDB.open(name, version);
req.onupgradeneeded = (e) => {
const db = e.target.result;
db.createObjectStore('trades', { keyPath: 'id' });
};
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
}
const db = await openDB('NovaDash');
const tx = db.transaction('trades', 'readwrite');
tx.objectStore('trades').add({ id: 1, symbol: 'BTC' });Server-Sent Events (SSE) : Flux unidirectionnel du serveur vers le client. Plus simple que WebSocket, reconnexion automatique, fonctionne avec HTTP standard. Idéal pour les notifications, les feeds en direct.
Quand choisir :
• WebSocket → bidirectionnel (chat, trading, jeu)
• SSE → serveur → client uniquement (notifications, prix live)
• Polling → fallback si WS/SSE impossibles
// ─── WebSocket ───
class CryptoSocket {
#ws; #url; #reconnectDelay = 1000;
constructor(url) { this.#url = url; this.connect(); }
connect() {
this.#ws = new WebSocket(this.#url);
this.#ws.onopen = () => {
console.log('Connecté');
this.#ws.send(JSON.stringify({
action: 'subscribe',
channels: ['BTC-USD', 'ETH-USD']
}));
};
this.#ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.updateUI(data);
};
this.#ws.onclose = () => {
console.log('Déconnecté — reconnexion...');
setTimeout(() => this.connect(), this.#reconnectDelay);
this.#reconnectDelay = Math.min(
this.#reconnectDelay * 2, 30000
);
};
}
send(data) {
if (this.#ws.readyState === WebSocket.OPEN) {
this.#ws.send(JSON.stringify(data));
}
}
updateUI(data) {
document.querySelector(`#${data.symbol}`)
.textContent = data.price;
}
}
const ws = new CryptoSocket('wss://stream.exchange.com');
// ─── Server-Sent Events ───
const source = new EventSource('/api/stream/prices');
source.onmessage = (event) => {
const price = JSON.parse(event.data);
console.log(`${price.symbol}: ${price.value}`);
};
// Événements nommés
source.addEventListener('alert', (e) => {
const alert = JSON.parse(e.data);
showNotification(alert.message);
});
source.onerror = () => console.log('SSE reconnexion...');
// source.close(); // FermerResizeObserver : Détecte les changements de taille d'un élément. Plus précis que
window.resize — observe un élément spécifique, pas toute la fenêtre.MutationObserver : Observe les changements du DOM (ajout/suppression d'éléments, attributs, texte). Utile pour les composants tiers, les extensions, le monitoring de changements dynamiques.
Avantage : Tous les observers sont asynchrones et performants — ils ne bloquent pas le thread principal contrairement aux événements scroll/resize classiques.
// ─── IntersectionObserver — Lazy loading ───
const lazyObserver = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
lazyObserver.unobserve(img); // Stop
}
});
},
{ rootMargin: '200px' } // Précharger 200px avant
);
document.querySelectorAll('img[data-src]')
.forEach(img => lazyObserver.observe(img));
// ─── Animate on scroll ───
const animObserver = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
entry.target.classList.toggle(
'visible', entry.isIntersecting
);
});
},
{ threshold: 0.2 } // 20% visible
);
document.querySelectorAll('.el-card')
.forEach(el => animObserver.observe(el));
// ─── ResizeObserver ───
const resizer = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width } = entry.contentRect;
entry.target.classList.toggle('compact', width < 400);
}
});
resizer.observe(document.querySelector('.dashboard'));
// ─── MutationObserver ───
const mutObserver = new MutationObserver((mutations) => {
mutations.forEach(m => {
if (m.type === 'childList') {
console.log('Nœuds ajoutés:', m.addedNodes.length);
}
});
});
mutObserver.observe(document.body, {
childList: true, subtree: true
});writeText() pour copier, readText() pour coller (demande permission).Notifications API : Affiche des notifications système (même quand l'onglet est inactif). Requiert la permission de l'utilisateur. Combinée avec les Service Workers pour les push notifications.
Fullscreen API : Passe un élément en plein écran. Utile pour les dashboards, les graphiques, les jeux et les présentations.
Vibration API : Fait vibrer l'appareil mobile. Utile pour les alertes de prix.
// ─── Clipboard API ───
// Copier du texte
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showToast('Copié !');
} catch (err) {
// Fallback pour HTTP
const ta = document.createElement('textarea');
ta.value = text;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
ta.remove();
}
}
// Bouton "copier l'adresse wallet"
copyBtn.onclick = () => copyToClipboard('0xABC...DEF');
// ─── Notifications API ───
async function sendNotification(title, body) {
if (Notification.permission === 'default') {
await Notification.requestPermission();
}
if (Notification.permission === 'granted') {
new Notification(title, {
body,
icon: '/img/logo.png',
badge: '/img/badge.png',
tag: 'price-alert', // Remplace la précédente
requireInteraction: true
});
}
}
sendNotification('BTC Alert', 'Bitcoin > 100K $ !');
// ─── Fullscreen API ───
const chart = document.getElementById('chart');
function toggleFullscreen(element) {
if (!document.fullscreenElement) {
element.requestFullscreen();
} else {
document.exitFullscreen();
}
}
document.addEventListener('fullscreenchange', () => {
chart.classList.toggle('fullscreen',
!!document.fullscreenElement);
});
// ─── Vibration (mobile) ───
navigator.vibrate?.(200); // Vibrer 200ms
navigator.vibrate?.([100, 50, 100]); // PatternÉvénements source (élément glissé) :
•
dragstart → début du drag•
drag → pendant le mouvement•
dragend → fin (drop ou annulation)Événements cible (zone de dépôt) :
•
dragenter → entre dans la zone•
dragover → au-dessus (preventDefault obligatoire !)•
dragleave → sort de la zone•
drop → élément relâchéDataTransfer : Objet qui transporte les données entre la source et la cible. Supporte du texte, du HTML, des fichiers.
// ─── Drag & Drop — Liste réordonneable ───
// Rendre les éléments draggables
document.querySelectorAll('.trade-item').forEach(item => {
item.draggable = true;
item.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', item.id);
item.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
});
item.addEventListener('dragend', () => {
item.classList.remove('dragging');
});
});
// Zone de dépôt
const dropzone = document.querySelector('.portfolio');
dropzone.addEventListener('dragover', (e) => {
e.preventDefault(); // OBLIGATOIRE pour autoriser le drop
e.dataTransfer.dropEffect = 'move';
// Trouver l'élément le plus proche pour insérer
const afterEl = getDragAfterElement(dropzone, e.clientY);
const dragging = document.querySelector('.dragging');
if (afterEl) {
dropzone.insertBefore(dragging, afterEl);
} else {
dropzone.appendChild(dragging);
}
});
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
console.log(`Déposé : ${id}`);
});
function getDragAfterElement(container, y) {
const elements = [...container.querySelectorAll(
'.trade-item:not(.dragging)')
];
return elements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset, element: child };
}
return closest;
}, { offset: Number.NEGATIVE_INFINITY }).element;
}Performance & Sécurité
JS AvancéOptimisations de performance, bonnes pratiques de sécurité et outils de mesure pour des applications JavaScript de qualité production.
performance.now(), performance.mark() et performance.measure().Lazy Loading : Charger les ressources à la demande. Images avec
loading="lazy", modules avec import() dynamique, composants au scroll.DOM Batching : Minimiser les accès au DOM. Regrouper les lectures puis les écritures. Utiliser
DocumentFragment pour les insertions multiples.Web Vitals :
• LCP (Largest Contentful Paint) < 2.5s
• FID (First Input Delay) < 100ms
• CLS (Cumulative Layout Shift) < 0.1
// ─── Performance API ───
performance.mark('start-fetch');
const data = await fetch('/api/data').then(r => r.json());
performance.mark('end-fetch');
performance.measure('API Call', 'start-fetch', 'end-fetch');
const [measure] = performance.getEntriesByName('API Call');
console.log(`API: ${measure.duration.toFixed(2)}ms`);
// ─── Import dynamique (code splitting) ───
button.onclick = async () => {
const { ChartModule } = await import('./chart.js');
ChartModule.render(data);
};
// ─── DOM Batching avec DocumentFragment ───
function renderList(items) {
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = `${item.name}: ${item.price}$`;
fragment.appendChild(li);
});
// UN SEUL accès DOM au lieu de N
document.getElementById('list').appendChild(fragment);
}
// ─── Virtual Scroll (principe) ───
function virtualScroll(container, items, itemHeight) {
const visibleCount = Math.ceil(
container.clientHeight / itemHeight
) + 2; // Buffer
container.onscroll = () => {
const scrollTop = container.scrollTop;
const startIdx = Math.floor(scrollTop / itemHeight);
const visibleItems = items.slice(
startIdx, startIdx + visibleCount
);
renderList(visibleItems);
// Spacer pour le scroll
container.style.paddingTop =
`${startIdx * itemHeight}px`;
};
}
// ─── requestIdleCallback ───
requestIdleCallback((deadline) => {
while (deadline.timeRemaining() > 0) {
// Tâches non urgentes (analytics, préchargement)
doBackgroundWork();
}
});innerHTML avec des données non fiables. Utiliser textContent ou sanitizer.CSRF : Requête forgée par un site tiers. Protection : tokens CSRF, cookie SameSite, vérification Origin/Referer.
Content Security Policy (CSP) : En-tête HTTP qui restreint les sources de scripts, styles, images. Bloque les injections même si XSS réussit.
Bonnes pratiques :
•
textContent au lieu de innerHTML• Valider côté client ET serveur
• HttpOnly + Secure sur les cookies sensibles
• CORS restrictif
// ─── XSS — Le problème ───
const userInput = '<img onerror="alert(1)" src=x>';
// ❌ DANGEREUX — exécute le script
element.innerHTML = userInput;
// ✅ SAFE — échappe automatiquement
element.textContent = userInput;
// ─── Sanitization manuelle ───
function sanitizeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
// <script> → <script>
}
// ─── Sanitizer API (moderne) ───
// const sanitizer = new Sanitizer();
// element.setHTML(userInput, { sanitizer });
// ─── Échapper pour les URL ───
const query = encodeURIComponent(userInput);
const url = `/search?q=${query}`;
// Empêche l'injection dans l'URL
// ─── Protection CSRF ───
async function secureFetch(url, options = {}) {
const csrfToken = document.querySelector(
'meta[name="csrf-token"]'
)?.content;
return fetch(url, {
...options,
headers: {
...options.headers,
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json'
},
credentials: 'same-origin' // Cookies same-origin
});
}
// ─── Validation d'entrée ───
function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!re.test(email)) throw new Error('Email invalide');
if (email.length > 254) throw new Error('Email trop long');
return email.toLowerCase().trim();
}
// ─── Stockage sécurisé ───
// ❌ Ne JAMAIS stocker en localStorage :
// - Tokens JWT sensibles
// - Mots de passe
// - Données personnelles en clair
// → Utiliser des cookies HttpOnly + SecureWeakMap : Map dont les clés sont des références faibles. Si l'objet clé n'a plus d'autre référence, il peut être GC. Pas énumérable, pas de
.size. Idéal pour associer des métadonnées à des objets DOM.WeakSet : Comme Set mais avec des références faibles. Utile pour marquer des objets (visité, traité...).
WeakRef (ES2021) : Référence faible vers un objet.
.deref() retourne l'objet ou undefined s'il a été GC. Utile pour les caches intelligents.
// ─── WeakMap — métadonnées sans memory leak ───
const metadata = new WeakMap();
function trackElement(element) {
metadata.set(element, {
clickCount: 0,
createdAt: Date.now()
});
element.addEventListener('click', () => {
const data = metadata.get(element);
data.clickCount++;
});
}
// Si l'élément est supprimé du DOM,
// les métadonnées sont automatiquement GC ✅
// ─── WeakMap — données privées de classe ───
const _private = new WeakMap();
class Wallet {
constructor(balance) {
_private.set(this, { balance, pin: null });
}
get balance() { return _private.get(this).balance; }
setPin(pin) { _private.get(this).pin = pin; }
}
const w = new Wallet(1000);
w.balance; // 1000
_private.get(w); // { balance: 1000, pin: null }
// Inaccessible sans la référence _private
// ─── WeakRef — Cache intelligent ───
class SmartCache {
#cache = new Map();
set(key, value) {
this.#cache.set(key, new WeakRef(value));
}
get(key) {
const ref = this.#cache.get(key);
if (!ref) return undefined;
const obj = ref.deref();
if (!obj) {
this.#cache.delete(key); // GC a nettoyé
return undefined;
}
return obj;
}
}
// ─── Éviter les memory leaks ───
// ❌ Event listener jamais supprimé
// element.addEventListener('click', handler);
// ✅ Nettoyer à la destruction
const controller = new AbortController();
element.addEventListener('click', handler, {
signal: controller.signal
});
// Plus tard : controller.abort(); // Supprime le listenerJSON.parse(JSON.stringify()) qui ne gère ni les dates, ni les undefined, ni les références circulaires.JSON avancé :
JSON.stringify accepte un replacer (filtre) et un spacer. JSON.parse accepte un reviver pour transformer les valeurs à la lecture.Intl (Internationalisation) : API native pour formater les nombres, monnaies, dates, listes et messages selon la locale. Plus besoin de librairies pour le formatage basique.
// ─── structuredClone ───
const original = {
user: { name: 'Julien' },
trades: [{ symbol: 'BTC', date: new Date() }],
meta: new Map([['version', 2]])
};
// Clone profond — indépendant de l'original
const clone = structuredClone(original);
clone.user.name = 'Alice';
original.user.name; // 'Julien' — non affecté ✅
clone.trades[0].date instanceof Date; // true ✅
// ─── JSON avancé ───
// Replacer — filtrer/transformer
const json = JSON.stringify(original, (key, value) => {
if (key === 'pin') return undefined; // Exclure
if (value instanceof Date) return value.toISOString();
return value;
}, 2); // 2 = indentation
// Reviver — transformer à la lecture
const parsed = JSON.parse(json, (key, value) => {
// Reconvertir les dates ISO en objets Date
if (typeof value === 'string' &&
/\d{4}-\d{2}-\d{2}T/.test(value)) {
return new Date(value);
}
return value;
});
// ─── Intl — Formatage localisé ───
// Monnaie
new Intl.NumberFormat('fr-FR', {
style: 'currency', currency: 'EUR'
}).format(95000); // "95 000,00 €"
new Intl.NumberFormat('en-US', {
style: 'currency', currency: 'USD',
notation: 'compact'
}).format(95000); // "$95K"
// Date
new Intl.DateTimeFormat('fr-FR', {
dateStyle: 'full', timeStyle: 'short'
}).format(new Date()); // "vendredi 28 février 2026 à 14:30"
// Relatif
new Intl.RelativeTimeFormat('fr', { numeric: 'auto' })
.format(-1, 'day'); // "hier"
// Liste
new Intl.ListFormat('fr', { type: 'conjunction' })
.format(['BTC', 'ETH', 'SOL']); // "BTC, ETH et SOL"Introduction à TypeScript
TYPESCRIPTTypeScript est un surensemble typé de JavaScript. Il compile vers du JavaScript standard.
Avantages :
• Détection des bugs avant l'exécution
• Autocomplétion IDE et documentation intégrée
• Refactoring sécurisé sur de gros projets
• Standard industrie SaaS (Angular, Next.js, Nest.js)
: Type après le nom d'une variable, paramètre ou propriété.Inférence : TypeScript peut souvent deviner le type automatiquement. Annotez surtout les paramètres de fonctions — les variables locales initiálisées n'en ont souvent pas besoin.
Interfaces TypeScript
TYPESCRIPTLes interfaces définissent la forme des objets. Elles sont le pilier de la structuration de données en TypeScript.
Interface vs type :
interface pour les objets (extensible avec extends, declaration merging). type pour les unions, intersections et types complexes.extends permet à une interface d'hériter des propriétés d'une ou plusieurs autres interfaces.Héritage multiple : Contrairement aux classes, une interface peut étendre plusieurs interfaces simultanément.
Types Avancés
TSUnion, literal types, keyof, typeof et discriminated unions.
Narrowing : TypeScript rétrécit automatiquement le type dans les branches conditionnelles après une vérification.
type Status = "active" | "pending" | "deleted";
function processId(id: string | number) {
if (typeof id === "string") {
id.toUpperCase(); // ✅ string ici
} else {
id.toFixed(2); // ✅ number ici
}
}
string ou number — TypeScript vérifie que seules ces valeurs sont utilisées.Combiné avec union : Créer des types d'état finis, très utiles pour les SaaS.
type Direction = "up" | "down" | "left" | "right";
// Type d'état SaaS
type OrderStatus =
| "cart"
| "checkout"
| "paid"
| "shipped"
| "delivered";
function move(dir: Direction) { }
move("up"); // ✅
move("jump"); // ❌ Erreur TS
typeof : Extrait le type TypeScript d'une valeur ou variable. Utile pour typer à partir de valeurs existantes sans redéfinir.
interface User { nom: string; age: number; }
type UserKey = keyof User; // "nom" | "age"
function getField<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// typeof
const config = { port: 3000, host: "localhost" };
type Config = typeof config;
// { port: number; host: string; }
C'est le pattern de base pour les state machines TypeScript.
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: string };
function render(state: AsyncState<User>) {
switch (state.status) {
case "success": return state.data; // ✅ data typée
case "error": return state.error; // ✅ error typée
}
}
Generics
TYPESCRIPTLes Generics permettent de créer des composants réutilisables tout en conservant la sécurité du typage.
<T> est un paramètre de type — un placeholder remplacé par le type réel à l'utilisation.Avantage vs any : Contrairement à
any, les generics conservent la relation entre les types. TypeScript peut vérifier l'utilisation des données.Pattern SaaS : Un type
ApiResponse<T> générique réutilisé pour toutes les réponses API garantit la cohérence.Utility Types
TSTypes utilitaires intégrés pour transformer les types existants.
Implémentation interne :
{ [P in keyof T]?: T[P] }nom: string;
email: string;
role: string;
}
// Mise à jour partielle
function updateUser(id: string, data: Partial<User>) {
// data peut avoir 0, 1 ou 3 propriétés
}
updateUser("1", { email: "new@mail.com" }); // ✅
Omit<T, K> : Exclut les propriétés listées dans K. L'inverse de Pick. Très utilisé pour créer des DTOs.
id: number; nom: string; email: string;
password: string; createdAt: Date;
}
// Aperçu public
type UserCard = Pick<User, "id" | "nom">;
// Création (sans id et createdAt auto)
type CreateUserDTO = Omit<User, "id" | "createdAt">;
// DTO public (sans mot de passe)
type PublicUser = Omit<User, "password">;
{ [key: string]: value }.Particulièrement utile : Pour des maps, caches, tables de correspondance.
type Scores = Record<string, number>;
const scores: Scores = { alice: 95, bob: 87 };
// Clés typées (union)
type Status = "active" | "pending" | "deleted";
type StatusConfig = Record<Status, { label: string; color: string }>;
const config: StatusConfig = {
active: { label: "Actif", color: "green" },
pending: { label: "En attente", color: "yellow" },
deleted: { label: "Supprimé", color: "red" },
};
Parameters<T> : Extrait les types des paramètres en tuple.
Awaited<T> : Extrait le type résolu d'une Promise.
return { id, nom: "Julien", role: "admin" };
}
// Extraire le type de retour
type FetchResult = ReturnType<typeof fetchUser>;
// Promise<{ id: string; nom: string; role: string }>
// Résoudre la Promise
type User = Awaited<FetchResult>;
// { id: string; nom: string; role: string }
React — Fondamentaux
Frameworks JSBibliothèque UI de Meta pour construire des interfaces déclaratives par composants. Approche fonctionnelle avec hooks. Écosystème : Next.js, React Router, Redux/Zustand, React Query.
props.Règles JSX :
• Retourner un seul élément racine (ou
<Fragment> / <>...</>)•
className au lieu de class•
htmlFor au lieu de for• Les expressions JS sont entre
{accolades}Convention : Nom en PascalCase. Un fichier par composant.
// Composant fonctionnel avec props
function UserCard({ name, role, avatar }) {
return (
<div className="card">
<img src={avatar} alt={name} />
<h3>{name}</h3>
<span className="badge">{role}</span>
</div>
);
}
// Utilisation
// Fragment (pas de div wrapper)
function Stats() {
return (
<>
Statistiques
Total : 42
>
);
}Syntaxe :
const [valeur, setValeur] = useState(initial)Règles importantes :
• Ne jamais modifier l'état directement (
count++ ❌)• Toujours utiliser le setter (
setCount(count + 1) ✅)• Pour les objets/tableaux : toujours créer une copie
• Callback pour état basé sur le précédent :
setCount(prev => prev + 1)
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [user, setUser] = useState({
name: ‘Julien’, score: 0
});
return (
Compteur : {count}
```
{/* Mise à jour d'objet (spread) */}
<button onClick={() => setUser(prev => ({
...prev,
score: prev.score + 10
}))}>
+10 points
</button>
</div>
```
);
}
Tableau de dépendances :
•
[] vide = exécuté une seule fois (montage)•
[var1, var2] = exécuté quand var1 ou var2 changent• Absent = exécuté à chaque rendu (rarement voulu)
Cleanup : La fonction retournée est appelée au démontage (ou avant la ré-exécution).
import { useState, useEffect } from 'react';
function CryptoPrice({ symbol }) {
const [price, setPrice] = useState(null);
const [loading, setLoading] = useState(true);
// Appel API au montage + quand symbol change
useEffect(() => {
setLoading(true);
fetch(`/api/crypto/${symbol}`)
.then(res => res.json())
.then(data => {
setPrice(data.price);
setLoading(false);
});
}, [symbol]); // ← dépendance
// Timer avec cleanup
useEffect(() => {
const id = setInterval(() => {
console.log(‘refresh…’);
}, 5000);
return () => clearInterval(id); // cleanup
}, []);
if (loading) return Chargement…
;
return {symbol} : {price} $
;
}if dans le JSX — on utilise l'opérateur ternaire ? : ou le && pour afficher conditionnellement.Listes : Utilisez
.map() pour transformer un tableau en éléments JSX. Chaque élément doit avoir une key unique et stable (jamais l'index sauf pour des listes statiques).Pourquoi key : React utilise les clés pour identifier quel élément a changé, été ajouté ou supprimé — c'est essentiel pour la performance du Virtual DOM.
function Dashboard({ user, transactions }) {
return (
<div>
{/* Condition ternaire */}
{user ? (
<h2>Bonjour {user.name}</h2>
) : (
<h2>Connectez-vous</h2>
)}
```
{/* Affichage conditionnel (&&) */}
{user?.isAdmin && (
<button>Panel Admin</button>
)}
{/* Liste avec map + key */}
<ul>
{transactions.map(tx => (
<li key={tx.id}>
{tx.label} — {tx.amount} €
</li>
))}
</ul>
{/* Liste vide */}
{transactions.length === 0 && (
<p>Aucune transaction</p>
)}
</div>
```
);
}Custom Hooks : Fonctions préfixées par
use qui encapsulent de la logique réutilisable. Elles peuvent utiliser tous les hooks React à l'intérieur.Convention :
useFetch, useLocalStorage, useDebounce...
import { useRef, useState, useEffect } from 'react';
// useRef — accès DOM
function SearchBar() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus(); // auto-focus
}, []);
return ;
}
// Custom Hook — logique réutilisable
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
// Utilisation du hook
function CryptoList() {
const { data, loading } = useFetch(’/api/cryptos’);
if (loading) return Chargement…
;
return data.map(c => {c.name}
);
}<Link> remplace <a> pour la navigation interne.Context API : Partage d'état global sans passer les props à travers chaque niveau de composants (prop drilling). Idéal pour les thèmes, l'authentification, la langue.
Quand utiliser Context vs Redux/Zustand : Context pour les données peu changeantes (thème, user). Redux/Zustand pour les données fréquemment mises à jour.
// ─── React Router ───
import { BrowserRouter, Routes, Route, Link }
from 'react-router-dom';
function App() {
return (
} />
} />
} />
} />
);
}
// ─── Context API ───
import { createContext, useContext, useState } from ‘react’;
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [dark, setDark] = useState(true);
return (
{children}
);
}
// Dans n’importe quel composant enfant
function Header() {
const { dark, setDark } = useContext(ThemeContext);
return ;
}Vue.js — Fondamentaux
Frameworks JSFramework progressif créé par Evan You. Syntaxe template intuitive, réactivité automatique et courbe d'apprentissage douce. Écosystème : Nuxt, Vue Router, Pinia, Vite.
.vue contient le template HTML, le script JS et le style CSS — tout est co-localisé.<script setup> : Syntaxe Composition API simplifiée (Vue 3.2+). Tout ce qui est déclaré dans le script est automatiquement disponible dans le template. Plus besoin de
return.Props : Déclarées avec
defineProps(). Les données parentales descendent vers les enfants via les props (flux unidirectionnel).
<!-- UserCard.vue -->
<script setup>
// Props typées
const props = defineProps({
name: { type: String, required: true },
role: { type: String, default: 'Dev' },
avatar: String
});
</script>
{{ name }}
{{ role }}
.value dans le script, mais pas dans le template (auto-unwrap).reactive() : Rend un objet ou tableau profondément réactif. Pas besoin de
.value. Ne pas réassigner l'objet entier.Quand utiliser quoi :
•
ref() pour les primitifs (string, number, boolean)•
reactive() pour les objets complexes• En pratique,
ref() pour tout fonctionne très bien
<script setup>
import { ref, reactive, computed } from 'vue';
// ref — valeurs primitives
const count = ref(0);
const name = ref(‘Julien’);
// reactive — objets
const user = reactive({
name: ‘Julien’,
score: 0,
portfolio: [‘BTC’, ‘ETH’]
});
// computed — valeur calculée (mise en cache)
const doubled = computed(() => count.value * 2);
// Méthodes
function increment() {
count.value++; // .value dans le script
user.score += 10; // pas de .value
}
{{ count }} × 2 = {{ doubled }}
{{ user.name }} : {{ user.score }} pts
v-show masque avec CSS (meilleur pour les toggles fréquents).v-for : Boucle sur un tableau ou objet. Toujours ajouter
:key unique.v-model : Binding bidirectionnel pour les formulaires — l'input modifie l'état ET l'état modifie l'input automatiquement.
v-bind (:) : Lie dynamiquement un attribut. Raccourci
:src="url"v-on (@) : Écoute un événement. Raccourci
@click="fn"
<script setup>
import { ref } from 'vue';
const search = ref(’’);
const cryptos = ref([
{ id: 1, name: ‘Bitcoin’, price: 95000 },
{ id: 2, name: ‘Ethereum’, price: 3200 },
{ id: 3, name: ‘Solana’, price: 180 }
]);
const isLoggedIn = ref(true);
Dashboard
Connectez-vous
-
{{ crypto.name }} — {{ crypto.price }} $
watchEffect() : Exécuté immédiatement et re-exécuté quand ses dépendances changent (auto-détection). Plus simple que
watch() quand on veut juste "réagir".Hooks de cycle de vie :
•
onMounted() — DOM prêt (équiv. componentDidMount)•
onUnmounted() — nettoyage (timers, subscriptions)•
onUpdated() — après chaque mise à jour du DOM
<script setup>
import { ref, watch, watchEffect,
onMounted, onUnmounted } from 'vue';
const symbol = ref(‘BTC’);
const price = ref(null);
// watch — observer un changement
watch(symbol, async (newVal, oldVal) => {
console.log(`${oldVal} → ${newVal}`);
const res = await fetch(`/api/price/${newVal}`);
price.value = await res.json();
});
// watchEffect — auto-détection
watchEffect(() => {
document.title = `${symbol.value} : ${price.value}`;
});
// Lifecycle hooks
onMounted(() => {
console.log(‘Composant monté, DOM prêt’);
// Initialiser un timer, chart, WebSocket…
});
let intervalId;
onMounted(() => {
intervalId = setInterval(() => {
// Refresh des prix
}, 5000);
});
onUnmounted(() => {
clearInterval(intervalId); // Nettoyage
});
<RouterLink> et <RouterView>. Supporte les paramètres dynamiques, les gardes de navigation et le lazy loading.Pinia : Store officiel de Vue (remplace Vuex). Plus simple, typé, et compatible avec les DevTools Vue.
Structure Pinia :
•
state → données (comme ref/reactive)•
getters → données calculées (comme computed)•
actions → méthodes (modifications de l'état)
// ─── Vue Router ───
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: ‘/’, component: () => import(’./Home.vue’) },
{ path: ‘/crypto/:id’, component: () =>
import(’./CryptoDetail.vue’) },
{ path: ‘/:pathMatch(.*)’, component: NotFound }
]
});
// Dans un composant
import { useRoute, useRouter } from ‘vue-router’;
const route = useRoute(); // route.params.id
const router = useRouter(); // router.push(’/home’)
// ─── Pinia Store ───
// stores/crypto.js
import { defineStore } from ‘pinia’;
export const useCryptoStore = defineStore(‘crypto’, {
state: () => ({
coins: [],
loading: false
}),
getters: {
topCoins: (state) =>
state.coins.slice(0, 10)
},
actions: {
async fetchCoins() {
this.loading = true;
const res = await fetch(’/api/coins’);
this.coins = await res.json();
this.loading = false;
}
}
});
// Utilisation dans un composant
const store = useCryptoStore();
store.fetchCoins();Angular — Fondamentaux
Frameworks JSFramework complet de Google basé sur TypeScript. Architecture orientée composants avec dependency injection, services et RxJS. Écosystème : Angular CLI, Angular Material, NgRx, Signals.
@Component qui lie un template HTML, une feuille de style et une logique.Structure : Contrairement à React/Vue, Angular utilise des fichiers séparés par défaut (template, style, logique, tests) — mais le mode inline est possible.
@Input() : Reçoit des données du parent (comme les props React/Vue).
@Output() : Émet des événements vers le parent.
Standalone (Angular 14+) :
standalone: true supprime le besoin de NgModules — plus simple et proche de React/Vue.
// user-card.component.ts
import { Component, Input, Output, EventEmitter }
from '@angular/core';
@Component({
selector: ‘app-user-card’,
standalone: true,
template: `<div class="card"> <img [src]="avatar" [alt]="name" /> <h3>{{ name }}</h3> <span class="badge">{{ role }}</span> <button (click)="onSelect.emit(name)"> Sélectionner </button> </div>`,
styles: [`.card { background: rgba(255,255,255,0.05); border-radius: 12px; padding: 16px; }`]
})
export class UserCardComponent {
@Input() name = ‘’;
@Input() role = ‘Dev’;
@Input() avatar = ‘’;
@Output() onSelect = new EventEmitter();
}
// Utilisation dans un template parent
// signal() : Crée un signal modifiable. Lecture avec
(), écriture avec .set(), .update(), .mutate().computed() : Signal dérivé, recalculé automatiquement quand ses dépendances changent (comme
computed en Vue).effect() : Exécute du code quand un signal change (comme
useEffect en React ou watchEffect en Vue).Avantage : Remplace le change detection de Zone.js — plus performant et plus prévisible.
import { Component, signal, computed, effect }
from '@angular/core';
@Component({
selector: ‘app-counter’,
standalone: true,
template: `<p>Compteur : {{ count() }}</p> <p>Double : {{ doubled() }}</p> <button (click)="increment()">+1</button> <button (click)="reset()">Reset</button>`
})
export class CounterComponent {
// Signal — état réactif
count = signal(0);
// Computed — dérivé automatique
doubled = computed(() => this.count() * 2);
// Modification
increment() {
this.count.update(c => c + 1);
}
reset() {
this.count.set(0);
}
constructor() {
// Effect — réaction aux changements
effect(() => {
console.log(`Count changed: ${this.count()}`);
});
}
}@if, @for, @switch remplacent les directives structurelles *ngIf, *ngFor. Plus lisible et performant.Binding :
•
[propriété]="expr" → property binding (parent → enfant)•
(event)="handler()" → event binding•
[(ngModel)]="var" → two-way binding (formulaires)•
{{ expr }} → interpolation textePipes : Transforment les données dans le template :
| date, | currency, | uppercase, | async. On peut créer des pipes personnalisés.
// dashboard.component.ts
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [FormsModule, CurrencyPipe, DatePipe],
template: `
<!-- Two-way binding formulaire -->
<input [(ngModel)]="search" placeholder="Filtrer" />
```
<!-- Nouvelle syntaxe @if (Angular 17+) -->
@if (isLoggedIn()) {
<h2>Bienvenue {{ userName() }}</h2>
} @else {
<p>Connectez-vous</p>
}
<!-- @for avec track obligatoire -->
@for (crypto of cryptos(); track crypto.id) {
<div class="crypto-row">
<span>{{ crypto.name }}</span>
<!-- Pipe currency -->
<span>{{ crypto.price | currency:'USD' }}</span>
<!-- Property + Event binding -->
<button
[disabled]="crypto.price === 0"
(click)="selectCrypto(crypto)">
Détails
</button>
</div>
} @empty {
<p>Aucune crypto trouvée</p>
}
<!-- Pipe date -->
<p>Mis à jour : {{ lastUpdate | date:'short' }}</p>
```
`
})@Injectable. Séparent la logique de l'affichage.Dependency Injection (DI) : Angular crée et injecte automatiquement les instances de services dans les composants via le constructeur ou
inject().providedIn: 'root' : Le service est un singleton global — une seule instance pour toute l'application. C'est le pattern le plus courant.
HttpClient : Service intégré pour les requêtes HTTP. Retourne des Observables RxJS (comme des Promises mais annulables et composables).
// services/crypto.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { signal } from '@angular/core';
@Injectable({ providedIn: ‘root’ })
export class CryptoService {
private http = inject(HttpClient);
// État partagé via signals
coins = signal([]);
loading = signal(false);
fetchCoins() {
this.loading.set(true);
this.http.get(’/api/coins’)
.subscribe({
next: (data) => {
this.coins.set(data);
this.loading.set(false);
},
error: (err) => {
console.error(err);
this.loading.set(false);
}
});
}
}
// Utilisation dans un composant
@Component({ … })
export class CryptoListComponent {
// inject() — méthode moderne (Angular 14+)
private cryptoService = inject(CryptoService);
coins = this.cryptoService.coins;
loading = this.cryptoService.loading;
ngOnInit() {
this.cryptoService.fetchCoins();
}
} Lazy Loading : Charge les modules/composants à la demande — essentiel pour les grosses applications SaaS. L'utilisateur ne télécharge que ce dont il a besoin.
Guards : Fonctions qui contrôlent l'accès aux routes.
canActivate pour protéger (ex: authentification), canDeactivate pour confirmer la navigation (ex: formulaire non sauvegardé).routerLink : Remplace les
<a href> pour la navigation interne — pas de rechargement de page.
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{ path: ‘’, component: HomeComponent },
{
path: ‘dashboard’,
// Lazy loading — chargé à la demande
loadComponent: () =>
import(’./dashboard/dashboard.component’)
.then(m => m.DashboardComponent),
// Guard — accès authentifié
canActivate: [authGuard]
},
{
path: ‘crypto/:id’,
loadComponent: () =>
import(’./crypto-detail/crypto-detail.component’)
.then(m => m.CryptoDetailComponent)
},
{ path: ‘**’, component: NotFoundComponent }
];
// auth.guard.ts — Guard fonctionnel
import { inject } from ‘@angular/core’;
import { Router } from ‘@angular/router’;
export const authGuard = () => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.isAuthenticated()) return true;
return router.parseUrl(’/login’);
};
// Dans un template
//
// Dashboard
//
//