- Apprendre
- Informatique et numérique
- Programmation
- JavaScript : DOM, événements et code asynchrone
- Classes et modules JavaScript
Classes et modules JavaScript et outils de développement
package.json, et utilise les outils courants d’un environnement de travail moderne : un linter et un formateur de code, un serveur de développement et un bundler, ainsi qu’un exécuteur de tests. Termine en découvrant la place de TypeScript et des frameworks front-end dans cette chaîne d’outils.DébutantUnité 9 sur 9 de la formation Formation : JavaScript : DOM, événements et code asynchrone
Ce que tu vas apprendre
- Utilise des classes pour créer des objets aux comportements communs, protéger leurs données et construire des hiérarchies par héritage, puis explique la chaîne de prototypes sur laquelle reposent les classes.
- Définis une classe et crée des objets avec newÉcris une classe avec un constructeur, des champs, des méthodes et des accesseurs en lecture et en écriture, crée des instances avec
newet veille à ce quethisdésigne le bon objet. - Protège les données avec des membres privés et partage des valeurs avec des membres statiquesMasque l’état interne d’un objet derrière des champs et des méthodes privés, et place dans des membres statiques les valeurs et fonctions utilitaires qui appartiennent à la classe dans son ensemble.
- Construis des hiérarchies de classes avec extends et superCrée des sous-classes qui héritent de comportements et les redéfinissent, appelle la classe parente avec
super, vérifie les types avecinstanceofet détermine quand l’héritage ou la composition convient le mieux. - Explique le rôle des prototypes dans les objets et les classesDécris comment JavaScript recherche les propriétés dans la chaîne de prototypes, comment les classes et
extendsétablissent cette chaîne et pourquoi les prototypes intégrés ne sont pas modifiés.
- Définis une classe et crée des objets avec new
- Répartis un programme en modules qui exportent et importent des fonctionnalités, exécute-les dans les navigateurs et dans Node.js, charge le code à la demande et donne à chaque module un rôle clair.
- Exporte et importe du code entre fichiersRépartis un programme en modules ES et relie-les avec des exportations nommées et par défaut, des importations renommées ou dans un espace de noms, et des réexportations, en comprenant le fonctionnement de la portée de module et des liaisons dynamiques.
- Exécute des modules dans le navigateur et dans Node.jsCharge des modules dans une page web avec
type="module"depuis un serveur local, résous les chemins et les noms nus des modules, et comprends comment Node.js choisit entre les modules ES et CommonJS. - Charge des modules à la demande et organise un projet en modulesUtilise l’importation dynamique avec
import()etawaitau niveau supérieur, répartis une application en modules ciblés et évite les problèmes liés aux effets de bord et aux importations circulaires.
- Exporte et importe du code entre fichiers
- Décris un projet dans
package.json, exécute ses tâches avec des scripts npm, puis installe, mets à jour et choisis les dépendances afin que chaque ordinateur obtienne le même ensemble de paquets fonctionnel.- Configure un projet avec npm et package.jsonCrée un projet avec
npm init, lis et modifie les principaux champs depackage.json, exécute des tâches avec des scripts npm etnpx, et lis les numéros de version sémantique. - Installe, mets à jour et choisis des dépendancesInstalle des paquets comme dépendances d’exécution ou de développement, lis les plages de versions, utilise
package-lock.jsonetnpm cipour obtenir des installations reproductibles, maintiens les dépendances à jour et choisis les paquets en toute sécurité.
- Configure un projet avec npm et package.json
- Utilise un linter et un formateur de code pour repérer les problèmes et garder un style cohérent, un serveur de développement et un bundler pour exécuter et livrer un projet, et un exécuteur de tests pour vérifier que le code fonctionne.
- Repère les problèmes avec un linter et mets le code en forme automatiquementUtilise ESLint pour trouver des bogues probables sans exécuter le code et Prettier pour garder une mise en forme cohérente, configure les deux et lance-les depuis des scripts et l’éditeur.
- Sers et regroupe les fichiers d’un projet avec un serveur de développement et un bundlerExplique le rôle des bundlers et des serveurs de développement, exécute le cycle de développement, compilation et prévisualisation avec Vite, et comprends le tree shaking, la minification, les noms de fichiers avec empreinte, les cartes des sources et les variables d’environnement.
- Vérifie le code automatiquement avec un exécuteur de testsÉcris des tests unitaires avec des assertions pour des fonctions et des classes, exécute-les avec l’exécuteur de tests intégré à Node.js ou avec Vitest, lis les échecs et conçois du code facile à tester.
- Repère les problèmes avec un linter et mets le code en forme automatiquement
- Explique ce que TypeScript et les frameworks front-end ajoutent à JavaScript, comment ils utilisent les modules, npm et les outils de compilation, et comment tous ces outils fonctionnent ensemble dans un projet.
- Explique ce que TypeScript apporte à JavaScriptDécris comment les types statiques de TypeScript repèrent les erreurs avant l’exécution du code, comment le code TypeScript devient du JavaScript et quand son adoption justifie cette étape supplémentaire.
- Situe les frameworks dans une chaîne d’outils moderne complèteExplique le rôle des frameworks front-end et comment ils s’appuient sur les modules, npm et les bundlers, puis réunis les outils de cette unité dans une méthode de travail courante.
- Explique ce que TypeScript apporte à JavaScript
Classes et modules JavaScript : cours
1Construis des objets qui partagent des comportements
Un objet ordinaire regroupe des valeurs liées. Une classe ajoute un moyen reproductible de créer des objets avec leurs propres données et des comportements partagés. Commence par les constructeurs et les méthodes, puis contrôle les modifications au moyen de champs privés et d’une interface publique. L’héritage permet à des classes apparentées de spécialiser ce comportement. La chaîne de prototypes explique comment JavaScript trouve les méthodes et pourquoi une instance peut utiliser du code stocké ailleurs.
1.1 Crée et utilise des instances de classe
Une classe décrit des objets de même structure et de même comportement. Chaque objet créé à partir de celle-ci est une instance. Utilise une classe lorsque plusieurs objets ont besoin des mêmes opérations ; un objet ordinaire suffit souvent pour une valeur de configuration ponctuelle.
Donne à chaque objet ses propres données
Par convention, les noms de classes utilisent PascalCase, comme Rectangle. Le constructor s’exécute chaque fois que new crée une instance. À l’intérieur, this désigne le nouvel objet. Une affectation à this.width stocke une propriété d’instance ; un paramètre nommé width seul n’est qu’une variable locale.
class Rectangle {
constructor(width, height) {
this.width = width; // cm
this.height = height;
}
area() {
return this.width * this.height;
}
perimeter() {
return 2 * (this.width + this.height);
}
}
const a = new Rectangle(3, 4);
const b = new Rectangle(5, 2);
a.area(); // 12
a.perimeter(); // 14
b.area(); // 10
Pour a.area(), JavaScript appelle la méthode en donnant à this la valeur a. Multiplier sa largeur, 3 cm, par sa hauteur, 4 cm, donne 12 cm². Le périmètre s’obtient en additionnant les deux côtés différents et en doublant cette somme, soit 14 cm. Modifier b.width ne modifie que b.
Les méthodes n’ont pas de mot-clé function et ne sont pas séparées par des virgules. Une classe a au plus un constructeur. Pour une classe sans extends, l’omission du constructeur donne un constructeur par défaut vide. new renvoie automatiquement l’instance créée.
Un champ déclare une propriété dans le corps de la classe : unit = "cm"; donne à chaque instance sa propre valeur. label; vaut initialement undefined. Dans une classe sans extends, les champs d’instance sont initialisés avant l’exécution du corps du constructeur.
Lis et affecte des valeurs par des accesseurs
Un accesseur en lecture s’exécute lorsqu’une propriété est lue. Avec get area() { return this.width * this.height; }, lis a.area sans parenthèses. C’est une alternative à la méthode ci-dessus, qui utilise a.area().
Un accesseur en écriture s’exécute lors d’une affectation et peut rejeter une entrée invalide. Il doit stocker la valeur séparément : set width(value) { this.width = value; } se rappelle sans cesse jusqu’au débordement de la pile. Stocke la valeur sous un autre nom, comme le champ privé #width.
Conserve l’objet receveur lors du passage d’une méthode
class Counter {
count = 0;
increment() { this.count++; }
}
const counter = new Counter();
const inc = counter.increment;
// inc(); // TypeError: this is undefined
const safeInc = () => counter.increment();
safeInc(); // counter.count is 1
Les méthodes de classe s’exécutent en mode strict. Un appel détaché n’a pas d’objet receveur avant le point : this vaut donc undefined. L’appelant d’une fonction de rappel peut aussi fournir un autre objet receveur. Une fonction fléchée enveloppante préserve l’appel sur counter ; counter.increment.bind(counter) crée une fonction liée. Un champ contenant une fonction fléchée, increment = () => { this.count++; };, capture également le this de l’instance.
Appeler Rectangle(3, 4) sans new lève une TypeError. Utiliser une classe avant que sa déclaration ne soit initialisée lève une ReferenceError. Les classes diffèrent ici des déclarations de fonctions, qui peuvent être appelées plus haut dans un fichier.
JSON conserve les données, pas le comportement de la classe. JSON.stringify(a) enregistre ses propriétés propres publiques de largeur et de hauteur. L’analyse de ce texte crée un objet ordinaire ; reconstruis une instance avec new Rectangle(data.width, data.height) pour utiliser ses méthodes.
Points clés
newcrée une instance et exécute son constructeur ;thisdésigne l’objet receveur d’un appel de méthode.- Les champs contiennent les données propres à chaque instance ; les méthodes ordinaires sont partagées.
- Les accesseurs en lecture se lisent comme des propriétés ; les accesseurs en écriture s’exécutent lors d’une affectation et nécessitent un stockage distinct.
- Utilise une fonction fléchée enveloppante ou une fonction liée lorsqu’une fonction de rappel doit conserver son objet receveur.
- JSON ne restaure pas les méthodes de classe lors de l’analyse des données enregistrées.
1.2 Protège l’état avec des champs privés
Un objet peut proposer une petite interface publique tout en gardant son stockage privé. C’est l’encapsulation. L’interface détermine quelles modifications sont valides ; la confidentialité seule ne vérifie pas la valeur transmise à un accesseur en écriture public.
Place la validation à l’entrée
Un champ privé commence par # et doit être déclaré dans le corps de la classe. Le code de cette classe le lit avec this.#target. Le code extérieur ne peut ni lire ni modifier t.#target : c’est une erreur de syntaxe, pas une lecture de propriété qui renvoie undefined.
class Thermostat {
static MIN = 10;
static MAX = 30;
#target = 20;
get target() { return this.#target; }
set target(value) {
if (!Number.isFinite(value) ||
value < Thermostat.MIN ||
value > Thermostat.MAX) {
throw new RangeError("Target must be between 10 and 30 °C");
}
this.#target = value;
}
}
const t = new Thermostat();
t.target = 22;
t.target; // 22
// t.target = 35; // throws RangeError
L’accesseur en lecture expose une valeur sans exposer son stockage. L’accesseur en écriture rejette d’abord les entrées non finies ou non numériques, puis les valeurs hors de l’intervalle de 10 à 30 °C, bornes incluses. Seule une valeur acceptée atteint l’affectation. Après une tentative rejetée de définir 35 °C, la consigne reste à 22 °C. Une chaîne numérique comme "22" échoue également au test Number.isFinite.
Les accesseurs en lecture et en écriture utilisent #target : l’affectation à t.target ne rappelle donc pas récursivement l’accesseur en écriture. Les noms target et #target sont distincts. Un trait de soulignement initial, comme dans _target, demande seulement aux autres programmeurs de traiter une propriété publique comme interne ; JavaScript permet toujours d’y accéder.
Les champs privés n’apparaissent pas dans Object.keys, for...in ou la sérialisation JSON. Ce thermostat n’a pas de champs propres publics, donc JSON.stringify(t) produit "{}". L’enregistrement d’un objet peut donc nécessiter une méthode publique explicite qui renvoie les données à enregistrer.
Garde les fonctions auxiliaires internes privées
Une méthode comme #check(value) { ... } peut contenir des étapes de validation et s’appelle dans la classe avec this.#check(value). Elle ne fait pas partie de l’interface publique. Dans la classe, #target in obj teste si un objet possède ce champ privé.
Une sous-classe ne peut pas utiliser directement le nom privé de sa classe parente. Elle peut utiliser l’accesseur public en lecture ou en écriture de la classe parente, qui s’exécute dans la classe ayant déclaré le champ. La classe parente conserve ainsi le contrôle de son propre état.
Place le travail partagé sur la classe
Un membre statique appartient à la classe. Thermostat.MIN vaut 10, tandis que t.MIN vaut undefined. Ces bornes statiques sont des champs publics ; static ne les rend pas en lecture seule à lui seul.
class Tracked {
static count = 0;
constructor() { Tracked.count++; }
}
new Tracked();
new Tracked();
new Tracked();
Tracked.count; // 3
Chaque constructeur incrémente un même compteur au niveau de la classe. Un champ d’instance donnerait au contraire un compteur distinct à chaque objet. Une méthode de fabrique statique, comme static square(side) { return new Rectangle(side, side); }, se place dans Rectangle et s’appelle avec Rectangle.square(5). Une fonction de comparaison statique peut être transmise à sort lorsqu’elle prend les objets comparés comme arguments.
Dans une méthode statique, this désigne la classe sur laquelle elle a été appelée ; ce n’est pas une instance de rectangle ayant une largeur. Un bloc d’initialisation static { ... } effectue l’initialisation une fois lors de la définition de la classe et peut accéder aux membres privés de cette classe.
Points clés
- La confidentialité des champs
#est imposée par JavaScript ; le trait de soulignement n’est qu’une convention. - Valide les modifications par l’interface publique avant de changer l’état privé.
- Les champs privés sont absents de l’énumération ordinaire des clés et de la sérialisation JSON.
- Les sous-classes utilisent l’interface publique de la classe parente pour agir sur son état privé.
- Appelle les membres statiques sur la classe ; ils ne contiennent pas les données propres à chaque instance.
- Un bloc d’initialisation statique s’exécute une fois lorsque sa classe est définie.
1.3 Spécialise une classe avec l’héritage
L’héritage modélise une relation « est un ». Un cercle est une forme : une classe Circle peut donc hériter de la description commune d’une forme tout en fournissant son propre calcul d’aire. Une sous-classe hérite de comportements sans copier chaque méthode dans son corps.
Initialise d’abord la classe parente
class Circle extends Shape fait de Circle une sous-classe et de Shape sa superclasse. Dans le constructeur d’une sous-classe, super(...) appelle le constructeur de la classe parente. Cet appel doit s’exécuter avant la première utilisation de this. Une sous-classe sans constructeur en reçoit un qui transmet ses arguments à la classe parente.
class Shape {
constructor(name) { this.name = name; }
area() { return 0; }
describe() {
return this.name + " with area " +
this.area().toFixed(2) + " cm²";
}
}
class Circle extends Shape {
constructor(radius) {
super("Circle");
this.radius = radius;
}
area() { return Math.PI * this.radius ** 2; }
}
class Rectangle extends Shape {
constructor(width, height) {
super("Rectangle");
this.width = width;
this.height = height;
}
area() { return this.width * this.height; }
}
L’appel new Circle(2).describe() donne "Circle with area 12.57 cm²". D’abord, super définit le nom. Le cercle stocke son rayon. La méthode héritée describe() appelle ensuite this.area(), qui trouve la méthode du cercle. toFixed(2) met le résultat en forme pour l’affichage. new Rectangle(3, 4).describe() donne "Rectangle with area 12.00 cm²".
Redéfinis ou étends une méthode
Une sous-classe redéfinit une méthode en déclarant une méthode de même nom. Elle peut appeler la version de la classe parente avec super.methodName().
class Square extends Rectangle {
constructor(side) {
super(side, side);
this.name = "Square";
}
describe() {
return super.describe() +
" (all sides " + this.width + " cm)";
}
}
new Square(5).describe();
// "Square with area 25.00 cm² (all sides 5 cm)"
super(side, side) initialise une largeur et une hauteur égales. La méthode d’aire héritée les multiplie et la méthode redéfinie ajoute des informations à la description héritée. Oublier les arguments nécessaires à la classe parente peut laisser ses propriétés à undefined ; appeler super() ne suffit pas si la classe parente a besoin de valeurs.
Le polymorphisme signifie qu’un même appel peut sélectionner des comportements différents. Une boucle peut appeler s.describe() sur des cercles et des rectangles ; chaque description appelle la méthode area() appropriée. Ajouter une forme avec sa propre méthode d’aire ne nécessite pas de nouveau cas dans cette boucle.
new Circle(2) instanceof Circle et new Circle(2) instanceof Shape sont tous deux vrais. Les membres statiques sont également hérités. La relation entre une instance et une classe suit sa chaîne de prototypes, que instanceof vérifie normalement.
Choisis une relation adaptée
Une classe ne peut étendre qu’une seule classe parente. Pour une relation « possède un », utilise la composition : une voiture possède un moteur et demande à this.engine.start() d’effectuer un travail. Le simple partage d’une fonction utilitaire ne justifie pas de dire qu’une classe sans lien est une sorte d’une autre. Un mixin copie un groupe de méthodes sur un prototype, par exemple avec Object.assign(Rectangle.prototype, serializable). De longues chaînes de classes parentes peuvent rendre les modifications difficiles à suivre.
Les classes intégrées peuvent aussi être étendues. Un constructeur ValidationError extends Error appelle super(message), définit this.name = "ValidationError" et peut stocker this.field = field. Un bloc catch peut utiliser err instanceof ValidationError pour traiter cet échec différemment et lire le message hérité.
Points clés
- Utilise
extendspour une relation « est un » et la composition pour une relation « possède un ». - Appelle
super(...)avec les arguments nécessaires à la classe parente avant d’utiliserthis. - Une méthode redéfinie peut réutiliser le comportement de la classe parente avec
super.methodName(). - Le polymorphisme permet à un même appel de méthode de fonctionner avec des classes apparentées.
1.4 Suis la recherche des propriétés dans les prototypes
Un objet possède un lien caché vers un autre objet, son prototype, ou vers null. La lecture d’une propriété absente suit ce lien. JavaScript poursuit la recherche jusqu’à trouver la propriété ou à atteindre null ; une recherche infructueuse renvoie undefined.
Distingue les valeurs stockées et héritées
const defaults = { theme: "light", fontSize: 16 };
const settings = Object.create(defaults);
settings.fontSize = 18;
settings.theme; // "light"
settings.fontSize; // 18
defaults.fontSize; // 16
Object.hasOwn(settings, "theme"); // false
"theme" in settings; // true
Object.keys(settings); // ["fontSize"]
Object.create(defaults) crée un objet vide dont le prototype est defaults. L’affectation à fontSize crée une propriété propre qui masque la valeur héritée. Elle laisse defaults.fontSize inchangé. Après delete settings.fontSize, la recherche atteint de nouveau la valeur héritée 16.
| Vérification | Propriétés prises en compte |
|---|---|
Object.hasOwn(obj, key) | Propriétés propres, énumérables ou non |
Object.keys(obj) | Clés de type chaîne propres et énumérables |
key in obj | Propriétés propres et héritées, énumérables ou non |
for...in | Clés de type chaîne énumérables, y compris héritées |
Une propriété propre absente peut néanmoins exister dans la chaîne. Les méthodes de classe ne sont pas énumérables, donc for...in ne les répertorie normalement pas. Utilise Object.getPrototypeOf(settings) pour lire son prototype. Privilégie ces méthodes de Object à l’ancien accesseur __proto__.
Observe comment les classes partagent les méthodes
Pour une instance de rectangle r, Object.getPrototypeOf(r) === Rectangle.prototype est vrai. Sa largeur est une propriété propre, tandis que area est normalement stockée sur Rectangle.prototype. Object.hasOwn(r, "area") est faux même si r.area() fonctionne. Un champ contenant une fonction fléchée est différent : il crée sur chaque instance une propriété propre contenant une fonction.
Une méthode héritée reçoit toujours comme this l’objet placé avant le point. La méthode d’aire partagée peut donc lire des dimensions différentes pour chaque rectangle sans qu’une méthode distincte soit stockée sur chaque objet.
Pour la hiérarchie des formes, la recherche suit ce chemin :
circle -> Circle.prototype -> Shape.prototype
-> Object.prototype -> null
La méthode d’aire du cercle est trouvée en premier. Sa description vient de Shape.prototype, et toString peut venir de Object.prototype. La présence de Shape.prototype dans la chaîne explique aussi pourquoi circle instanceof Shape est vrai.
Le nom Rectangle.prototype désigne le prototype donné aux instances de rectangle. Il ne désigne pas le prototype caché de la fonction Rectangle elle-même ; pour cette classe, il s’agit de Shape lorsqu’elle étend Shape, ou de Function.prototype pour une classe sans superclasse.
Reconnais l’ancien code utilisant des constructeurs
function Rectangle(width, height) {
this.width = width;
this.height = height;
}
Rectangle.prototype.area = function () {
return this.width * this.height;
};
Les fonctions constructeurs utilisent la même structure de base reliant l’instance à son prototype. La syntaxe des classes ajoute des règles : new est obligatoire, le corps s’exécute en mode strict, les méthodes ne sont pas énumérables et les membres privés sont disponibles. typeof Rectangle vaut "function" dans les deux formes.
Les tableaux trouvent map sur Array.prototype ; les méthodes de chaînes viennent de String.prototype. Évite d’ajouter des méthodes personnalisées aux prototypes intégrés : des bibliothèques et de futures normes peuvent utiliser les mêmes noms avec des comportements différents. L’utilisation existante de flatten par une bibliothèque a contribué au choix du nom flat pour la méthode de tableau standard.
Une affectation à une propriété de données ordinaire crée ou modifie une propriété propre, mais un accesseur en écriture hérité peut intercepter l’affectation. Modifier le prototype d’un objet existant avec Object.setPrototypeOf est autorisé, mais peut nuire aux performances ; choisis le prototype lors de la création de l’objet.
Points clés
- La lecture d’une propriété la recherche dans l’objet, puis dans ses prototypes, jusqu’à atteindre
null. - Une propriété propre peut masquer une valeur héritée sans modifier le prototype.
- Le fait qu’une propriété soit propre et le fait qu’elle soit énumérable sont deux questions distinctes.
- Les méthodes de classe ordinaires se trouvent sur le prototype des instances et partagent leur comportement.
- Une méthode héritée utilise l’instance receveuse comme
this. - Les fonctions constructeurs et les classes reposent sur le même mécanisme de prototypes.
- Laisse les prototypes intégrés inchangés pour éviter les conflits de noms et de comportements.
2Relie les fichiers avec des modules ES
Les modules permettent à un programme de dépasser le cadre d’un seul script tout en gardant chaque fichier compréhensible. Les exportations décrivent ce qu’un fichier propose et les importations montrent ce dont il a besoin. Découvre comment fonctionne ce lien, comment les navigateurs et Node.js chargent les modules et comment ne charger une fonctionnalité que lorsqu’elle est demandée. Des frontières claires entre les données, le stockage et les mises à jour de la page facilitent aussi les tests et aident à prévenir les dépendances circulaires.
2.1 Exporte des fonctionnalités et importe leurs liaisons
Un module ES possède sa propre portée. Ses déclarations de niveau supérieur restent dans le fichier, sauf si elles sont exportées. Les fichiers importateurs demandent des fonctionnalités précises, ce qui rend les dépendances visibles sans tout placer dans des variables globales.
Fais correspondre la forme d’exportation
// geometry.js
export const round2 = (n) => Math.round(n * 100) / 100;
export function circleArea(r) {
return Math.PI * r * r;
}
// shapes.js
import { circleArea, round2 } from "./geometry.js";
export default class Circle {
constructor(r) { this.r = r; }
area() { return round2(circleArea(this.r)); }
}
// main.js
import Circle from "./shapes.js";
console.log(new Circle(3).area()); // 28.27
Le module d’entrée importe une classe. Le module de la classe importe deux fonctions utilitaires. Pour un rayon de 3 cm, l’aire est d’environ 28,274 cm² ; la fonction d’arrondi produit 28,27. Le module d’entrée n’accède pas automatiquement aux fonctions utilitaires utilisées dans un autre module.
| Exportation | Importation correspondante |
|---|---|
export const MAX = 30; | import { MAX } from "./limits.js"; |
export default class Circle {} | import Circle from "./shapes.js"; |
Une exportation par défaut et l’exportation nommée round2 | import Circle, { round2 } from "./shapes.js"; |
Un module peut avoir de nombreuses exportations nommées, mais au plus une exportation par défaut. Les importations nommées utilisent le nom exporté entre accolades ; le nom local d’une importation par défaut est choisi par le fichier importateur. Écrire import { Circle } ne demande pas l’exportation par défaut, même lorsque la classe exportée se nomme Circle.
Renomme, regroupe et réexporte
import { circleArea as areaOfCircle } from "./geometry.js"; donne à une fonctionnalité importée un nom local différent. export { circleArea as area }; change son nom à l’exportation. Une liste d’exportation peut aussi exposer des déclarations faites plus haut dans le fichier.
import * as geometry from "./geometry.js"; crée un objet d’espace de noms de module. Appelle geometry.circleArea(3). Un espace de noms inclut une exportation par défaut sous la propriété default si le module en possède une.
Un fichier peut regrouper les exportations d’autres fichiers : export { circleArea } from "./geometry.js"; transmet une exportation nommée. export * from "./geometry.js"; transmet les exportations nommées, mais pas l’exportation par défaut de ce module.
Comprends l’évaluation partagée et les liaisons dynamiques
Les modules s’exécutent en mode strict et leur this au niveau supérieur vaut undefined. Les dépendances sont évaluées avant le corps propre au module importateur. Importer le même module résolu depuis plusieurs fichiers réutilise un seul module évalué et son état.
Supposons qu’un module exporte let count = 0 et que increment() le modifie. Après l’appel de increment() par un fichier importateur, son count importé vaut 1. Les importations sont des liaisons dynamiques, pas des copies instantanées. Le fichier importateur ne peut pas réaffecter la liaison elle-même. Si une exportation est un objet, il reste possible de modifier une de ses propriétés et les autres fichiers importateurs voient cette modification.
Place les déclarations d’importation statique au niveau supérieur du module. Une importation statique dans un if ou une fonction est une erreur de syntaxe ; import() dynamique permet un chargement ultérieur. Inclus .js dans les chemins relatifs des modules ES pour le navigateur et Node.js : ces systèmes de chargement n’ajoutent pas l’extension. Une erreur de chemin ou une confusion entre exportations nommées et par défaut empêche le chargement du module : corrige donc l’importation avant de chercher un bogue dans le code censé s’exécuter ensuite.
Points clés
- Les modules gardent locales les déclarations de niveau supérieur, sauf si elles sont exportées.
- Les importations nommées utilisent des accolades ; une importation par défaut n’en utilise pas.
- Un espace de noms regroupe les exportations, tandis que les réexportations transmettent l’interface d’un module.
- Les importations sont des liaisons dynamiques en lecture seule vers l’état partagé d’un module.
- Les importations statiques se placent au niveau supérieur et les chemins relatifs des modules ES nécessitent une extension.
2.2 Charge des modules dans les navigateurs et dans Node.js
La même syntaxe de modules ES s’exécute dans un navigateur et dans Node.js. Chaque environnement doit néanmoins savoir qu’un fichier est un module et possède sa propre façon de résoudre les chemins demandés.
Démarre un module dans le navigateur
<script type="module" src="main.js"></script>
Charge uniquement le module d’entrée avec une balise script. Ses importations récupèrent automatiquement les autres fichiers. Les scripts de module sans async sont différés : ils s’exécutent après l’analyse du HTML, dans l’ordre du document. Les éléments de la page auxquels ils accèdent sont alors disponibles.
Sers le dossier sur HTTP avec un serveur local, comme python3 -m http.server ou npx serve, puis ouvre son adresse localhost. Ouvrir le HTML depuis le disque fait généralement échouer les requêtes de modules en raison des règles de sécurité du navigateur. Le serveur doit aussi renvoyer le JavaScript avec un type MIME adapté, comme text/javascript.
Les déclarations d’un module ne deviennent pas globales. Un gestionnaire en ligne comme onclick="save()" ne peut pas trouver une fonction locale à un module. Attache plutôt le gestionnaire depuis le JavaScript :
const button = document.querySelector("#save");
function save() { console.log("Saved"); }
button.addEventListener("click", save);
Résous les chemins et les noms de paquets
Le spécificateur de module est la chaîne qui nomme le module. ./geometry.js est relatif au fichier importateur ; ../geometry.js remonte d’un dossier. Un chemin qui commence par / part de la racine du site web, tandis qu’une URL HTTPS complète indique son emplacement entier.
Un nom nu comme "date-fns" n’a pas de préfixe de chemin. Un navigateur ne peut pas le rechercher dans node_modules. Une carte d’importation placée avant les scripts de module associe un nom nu à une URL. Un serveur de développement ou un bundler peut à la place résoudre les paquets installés et les préparer pour le navigateur. Installer un paquet ne modifie pas à lui seul le système de chargement du navigateur.
Explicite le format de module de Node.js
| Fichier ou réglage | Format dans Node.js |
|---|---|
.mjs | Module ES |
.cjs | CommonJS |
.js avec "type": "module" | Module ES |
.js avec "type": "commonjs" | CommonJS |
Node.js lit type dans le fichier package.json parent le plus proche. Sans type explicite, les versions actuelles peuvent détecter la syntaxe des modules ES dans un fichier .js autrement ambigu. Définis "type": "module" dans un nouveau projet de modules ES afin que les outils s’accordent sur le format voulu. Les extensions .mjs et .cjs prévalent sur ce réglage.
Avec les trois fichiers de l’exemple du cercle et { "type": "module" }, node main.js affiche 28.27. Les mêmes fichiers fonctionnent avec un script de module dans le navigateur servi sur HTTP.
Reconnais CommonJS et les modules intégrés
CommonJS est l’ancien système de modules de Node.js. Il utilise require() pour charger de manière synchrone et module.exports pour fournir des valeurs. Un module ES peut importer module.exports d’un paquet CommonJS comme exportation par défaut. Les versions actuelles de Node.js peuvent aussi charger avec require des graphes de modules ES synchrones ; un await au niveau supérieur dans ce graphe empêche le chargement synchrone.
Dans un module ES, utilise import { readFile } from "node:fs/promises"; pour un module intégré de Node.js. require, module et __dirname sont des variables CommonJS, pas des variables globales de module ES. import.meta.url donne l’URL du module ; les versions récentes de Node.js fournissent aussi import.meta.dirname pour les modules correspondant à des fichiers locaux.
Si une instruction d’importation est rejetée parce qu’elle se trouve hors d’un module, examine le réglage de chargement : type="module" dans le HTML du navigateur, ou le type du paquet ou l’extension dans Node.js.
Points clés
- Un module d’entrée dans le navigateur utilise
type="module"et son exécution est normalement différée. - Utilise un serveur local et attache les gestionnaires d’événements depuis le module.
- Les navigateurs ont besoin d’une carte d’importation ou d’outils de compilation pour résoudre les noms nus des paquets.
- Node.js utilise les extensions de fichiers et le champ
typedu paquet le plus proche. - CommonJS utilise
requireetmodule.exports; les modules ES utilisentimportetexport.
2.3 Charge les fonctionnalités à la demande et sépare les responsabilités
Une importation statique déclare une dépendance nécessaire au chargement d’un module. L’importation dynamique est une expression : elle commence le chargement lorsque l’exécution l’atteint et renvoie une promesse. Utilise-la pour une fonctionnalité dont de nombreuses visites n’ont jamais besoin.
Attends l’objet d’espace de noms
const button = document.querySelector("#show-chart");
button.addEventListener("click", async () => {
try {
const { drawChart } = await import("./chart.js");
drawChart(measurements);
} catch (err) {
console.error("Could not load the chart module", err);
}
});
Ici, measurements représente les données collectées par la page. Le premier clic charge le module de graphique et appelle sa fonction exportée. Les clics suivants réutilisent le module évalué. La promesse peut être rejetée en cas d’erreur de chargement ou d’évaluation : le gestionnaire intercepte donc cette erreur.
import("./chart.js").drawChart tente de lire une propriété sur la promesse, pas sur le module chargé. Attends la promesse ou utilise .then. Pour une exportation par défaut, accède à la propriété default de l’espace de noms : const { default: Chart } = await import("./chart.js");. Contrairement à une déclaration statique, cette expression peut se trouver dans une fonction ou une condition, et son chemin peut être calculé à l’exécution.
Attends pendant l’initialisation du module
Un module ES peut utiliser await au niveau supérieur, hors d’une fonction :
const response = await fetch("./config.json");
export const config = await response.json();
Les modules qui dépendent de celui-ci attendent la fin de son évaluation avant d’exécuter leur propre corps. Les modules indépendants peuvent continuer. Une requête de configuration lente retarde donc les modules qui en dépendent, mais pas nécessairement toutes les parties du programme. Les scripts classiques et CommonJS n’autorisent pas await au niveau supérieur ; utilise cette fonctionnalité du module pour les initialisations dont les modules dépendants ont réellement besoin en premier.
Donne à chaque module un rôle clair
Une application de tâches peut utiliser ces séparations :
| Module | Responsabilité |
|---|---|
todo.js | Exporter la classe Todo |
storage.js | Charger et enregistrer les données avec localStorage |
ui.js | Afficher la liste dans la page |
main.js | Relier les modules et les gestionnaires d’événements |
Les modules de données et de stockage n’importent pas l’interface utilisateur. Ils peuvent être testés et réutilisés sans créer d’éléments de page. Garder le calcul et le stockage hors des gestionnaires d’événements facilite aussi la localisation des erreurs.
L’état privé de niveau supérieur est partagé entre les fichiers importateurs. Dans let nextId = 1; export function createId() { return nextId++; }, la variable reste privée tandis que la fonction expose un accès contrôlé. Deux fichiers importateurs qui l’appellent chacun une fois obtiennent 1 et 2, et non deux compteurs distincts commençant à 1.
Contrôle le travail effectué à l’importation
import "./setup.js"; est une importation pour ses effets de bord : elle charge et évalue le module sans demander de noms locaux. Les effets de bord peuvent être utiles à l’initialisation, mais un module réutilisable est plus facile à tester lorsque son niveau supérieur définit des fonctionnalités et que ses fonctions démarrent explicitement le travail.
Un fichier d’index ou fichier barrel regroupe des réexportations. Il peut simplifier une interface, mais un fichier barrel volumineux peut introduire du code supplémentaire et masquer un cycle. Une dépendance circulaire apparaît lorsque des modules dépendent les uns des autres, directement ou par l’intermédiaire d’autres modules. Un cycle peut fonctionner, mais lire une liaison avant que le module qui l’exporte l’ait initialisée provoque une ReferenceError. Déplace les définitions partagées dans un troisième module importé par les deux côtés pour supprimer ce cycle de dépendance.
Points clés
import()renvoie une promesse dont le résultat sera un espace de noms de module et peut s’exécuter dans une fonction.- Attends le chargement dynamique et gère les promesses rejetées.
- Await au niveau supérieur retarde l’évaluation des modules dépendants.
- Sépare les données, le stockage, le rendu et la connexion des événements dans des modules ciblés.
- Un module évalué unique peut contenir un état privé partagé par tous les fichiers qui l’importent.
- Déplace les définitions partagées dans un troisième module pour rompre une dépendance circulaire.
3Exécute un projet reproductible avec npm
Un projet a besoin d’une description claire de ses commandes et des paquets dont il dépend. npm conserve ces informations dans des fichiers de paquets et installe le code qu’ils décrivent. Apprends à lire les scripts avant de les exécuter, à distinguer les outils de développement des dépendances du produit et à utiliser les plages de versions avec un fichier de verrouillage. Ces choix permettent à un autre ordinateur de recréer le projet sans copier un dossier de paquets installés.
3.1 Décris et exécute un projet npm
npm est le gestionnaire de paquets fourni avec Node.js. Ce nom désigne à la fois un outil en ligne de commande et son registre public de paquets. Un paquet est un dossier décrit par package.json ; la racine d’un projet est le dossier qui contient ce fichier.
Crée la description du projet
Exécute node -v et npm -v pour vérifier les outils installés. Place-toi dans le dossier prévu pour le projet avant d’exécuter npm init. La commande interactive pose des questions ; npm init -y accepte les valeurs par défaut. Modifie ensuite le fichier pour décrire le projet.
{
"name": "shape-lab",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"start": "node src/main.js",
"test": "node --test",
"lint": "eslint ."
},
"dependencies": {},
"devDependencies": {}
}
Il s’agit de JSON, pas d’un littéral d’objet JavaScript. Les clés et les chaînes utilisent des guillemets doubles. Les commentaires et les virgules finales sont interdits. Une erreur d’analyse syntaxique peut arrêter npm avant qu’il n’exécute une commande.
Le name est en minuscules et utilisable dans une URL ; les noms des paquets publiés comportent au maximum 214 caractères. version décrit ce projet, pas sa version de Node.js. private: true empêche une publication accidentelle sur npm. type: "module" indique à Node.js de charger les fichiers .js du paquet comme des modules ES.
Les paquets publiés peuvent utiliser main et exports pour définir des points d’entrée destinés à leurs utilisateurs. Une entrée facultative engines, comme { "node": ">=22" }, indique les versions de Node.js prises en charge. Lis ces exigences lorsqu’un projet échoue sur une installation plus ancienne.
Utilise les scripts comme menu des tâches
npm run énumère les scripts disponibles. Dans cet exemple, npm run lint exécute eslint ., npm start exécute l’application et npm test exécute l’exécuteur de tests. start et test disposent de ces formes courtes ; les autres noms de tâches utilisent normalement npm run.
npm ajoute temporairement le répertoire node_modules/.bin du projet au chemin de recherche des commandes. Un outil installé localement peut donc être nommé directement dans un script. Toutes les personnes qui utilisent le projet exécutent le même outil enregistré au lieu de dépendre d’une installation globale.
Les arguments supplémentaires suivent -- : npm run lint -- --fix transmet --fix à ESLint. Le script donne un nom stable à la tâche même si l’équipe change l’outil utilisé derrière celle-ci. Commence par le champ scripts pour apprendre à travailler sur un projet inconnu.
Lis les numéros de version
Le versionnement sémantique utilise MAJEURE.MINEURE.CORRECTIF. À partir de 2.4.1, une correction de bogue compatible donne 2.4.2 ; une fonctionnalité compatible donne 2.5.0 ; un changement incompatible donne 3.0.0. Une mise à niveau majeure peut nécessiter des modifications du code qui utilise le paquet. Avant 1.0.0, un projet est en début de développement et les versions publiées peuvent changer plus librement.
Exécute occasionnellement une commande de paquet
npx serve exécute la commande installée si elle est disponible localement. Sinon, npx peut proposer de télécharger temporairement le paquet et de l’exécuter. C’est utile pour un outil qui n’a pas besoin de devenir une dépendance permanente du projet. Vérifie le nom exact du paquet et la fiabilité du code avant de l’installer ou de l’exécuter : les commandes de paquets et les scripts d’installation peuvent s’exécuter avec tes autorisations.
Une commande exécutée dans le mauvais dossier peut échouer parce que package.json manque, ou une installation peut créer des fichiers de paquets à un endroit non prévu. Vérifie d’abord le dossier courant. Garde les scripts compréhensibles et évite les astuces de commande propres à un système d’exploitation lorsque le projet doit fonctionner sur plusieurs systèmes.
Points clés
package.jsondécrit le projet, les scripts et les dépendances en JSON valide.private: trueempêche la publication sur npm ;typedéfinit le format de module de Node.js.- Utilise des scripts npm pour exécuter de façon cohérente les outils installés localement.
- Les versions sémantiques distinguent les changements incompatibles, les fonctionnalités compatibles et les correctifs.
npxpeut exécuter une commande locale ou en télécharger une temporairement.
3.2 Installe et mets à jour les bonnes versions des paquets
Une dépendance est du code dont un projet dépend. npm enregistre les dépendances directes dans package.json, installe les paquets dans node_modules et enregistre l’arbre exact des dépendances installées dans package-lock.json.
Sépare le code du produit des outils de développement
npm install date-fns ajoute un paquet d’exécution à dependencies. npm install --save-dev eslint ajoute un outil de développement à devDependencies ; -D est la forme courte. Les linters, formateurs de code, exécuteurs de tests, bundlers et TypeScript appartiennent normalement à la liste de développement.
Lorsqu’un autre projet installe un paquet publié, npm installe les dépendances de ce paquet, mais pas ses dépendances de développement. Une application de navigateur peut tout de même regrouper les dépendances d’exécution dans ses fichiers finaux, même si l’outil de compilation lui-même est une devDependency.
npm install sans nom de paquet installe les dépendances indiquées pour le projet. npm uninstall <package> supprime un paquet et son entrée. Après l’installation de date-fns, Node.js et les outils de compilation peuvent résoudre import { format } from "date-fns";. Un navigateur seul a toujours besoin d’un mécanisme de résolution, comme une carte d’importation ou un serveur de développement.
Lis la plage autorisée
| Entrée | Versions stables autorisées |
|---|---|
^4.17.21 | Au moins 4.17.21, inférieure à 5.0.0 |
~4.17.21 | Au moins 4.17.21, inférieure à 4.18.0 |
4.17.21 | Exactement 4.17.21 |
^0.3.1 | Au moins 0.3.1, inférieure à 0.4.0 |
L’accent circonflexe est la valeur par défaut habituelle de npm. Pour les versions à partir de 1.0.0, il autorise les mises à jour mineures et les correctifs compatibles. En dessous de 1.0.0, il est plus strict. Pour ^2.3.0, 2.3.5 et 2.9.0 conviennent ; 2.2.9 est trop ancienne et 3.0.0 franchit la limite de version majeure.
Une plage autorisée ne désigne pas une version installée précise. Le fichier de verrouillage le fait, y compris pour les dépendances transitives, c’est-à-dire les paquets dont les paquets choisis ont besoin.
Recrée une installation
Partage package.json et package-lock.json avec le projet. Laisse npm gérer le fichier de verrouillage. Le dossier généré node_modules peut être reconstruit et n’est pas le fichier à partager.
Utilise npm install pendant le développement, lorsque les enregistrements des dépendances peuvent changer. Utilise npm ci pour une installation propre et reproductible : la commande exige le fichier de verrouillage, supprime le dossier node_modules existant, installe l’arbre verrouillé et ne réécrit aucun des deux fichiers de paquets. Elle échoue si le fichier de paquet et le fichier de verrouillage ne correspondent pas. Corrige cet écart volontairement pendant le développement ; supprimer le fichier de verrouillage peut modifier silencieusement de nombreuses versions.
Mets à jour pour une raison précise
npm outdated compare les versions actuelles, souhaitées et les plus récentes. La version souhaitée est la plus récente autorisée par la plage déclarée ; la plus récente peut être une version majeure hors de cette plage. npm update met à jour dans les plages autorisées et enregistre l’arbre obtenu. Pour franchir volontairement une limite de version majeure, lis le journal des modifications, exécute npm install <package>@latest et vérifie le projet avec ses tests.
Installe les outils du projet localement afin que leurs versions soient enregistrées. Avant d’ajouter une dépendance, vérifie sa maintenance, son adoption, sa licence et si une fonctionnalité intégrée résout déjà le problème. structuredClone() répond aux besoins de copie profonde qu’il prend en charge ; Intl.DateTimeFormat met en forme les dates sans paquet de gestion des dates.
npm audit signale les vulnérabilités connues des versions installées. Lis le rapport pour comprendre le code concerné et son utilisation. npm audit fix peut appliquer des correctifs compatibles, mais ne prouve pas que toutes les vulnérabilités sont résolues ni qu’une mise à jour préserve le comportement du projet.
Points clés
- Utilise
dependenciespour le code d’exécution etdevDependenciespour les outils de développement et de compilation. - Les plages de versions autorisent certaines versions ; le fichier de verrouillage enregistre les versions exactes installées.
- Partage les deux fichiers de paquets et reconstruis
node_modules. npm ciinstalle les versions du fichier de verrouillage sans le modifier et rejette les fichiers de paquets qui ne correspondent pas.npm updatereste dans les plages déclarées ; les mises à niveau majeures nécessitent un choix délibéré.- Lis les rapports d’audit et vérifie le comportement après les mises à jour des dépendances.
4Vérifie et prépare le code avec des outils de développement
Les différents outils répondent à différentes questions sur un projet. Un linter recherche du code suspect, un formateur le met en forme, des tests vérifient son comportement et une compilation prépare les fichiers du navigateur pour leur publication. Un serveur de développement accélère le cycle de modification. Apprends ce que chaque résultat t’indique et ce qu’il ne vérifie pas, afin de ne pas prendre la réussite d’une commande de mise en forme ou l’affichage d’une page de développement pour une preuve que toute l’application fonctionne.
4.1 Repère les problèmes du code et mets-le en forme de façon cohérente
Un linter lit le code source sans l’exécuter et signale les bogues probables ou les constructions risquées. Un formateur de code réécrit la présentation de manière cohérente. ESLint et Prettier sont souvent utilisés ensemble, car ces rôles résolvent des problèmes différents.
Configure les vérifications
npm init @eslint/config@latest pose des questions sur le projet, installe des paquets de développement et crée un fichier de configuration. ESLint utilise actuellement la configuration à plat, généralement dans eslint.config.js.
import js from "@eslint/js";
import globals from "globals";
export default [
js.configs.recommended,
{
languageOptions: { globals: globals.browser },
rules: {
eqeqeq: "error",
"no-unused-vars": "warn",
},
},
];
La configuration recommandée active des règles de détection des bogues. Les variables globales du navigateur indiquent à ESLint que des noms comme document existent. Cela évite de considérer une fonctionnalité réelle de l’environnement comme un nom non défini.
Les niveaux de sévérité des règles sont "off", "warn" et "error", également écrits 0, 1 et 2. Les erreurs produisent un code de sortie indiquant un échec ; les avertissements seuls ne font pas échouer la commande par défaut. Un avertissement reste une information à lire, même si le script se termine avec succès.
Lis un rapport du linter
function total(items) {
let sum = 0;
let unused = 5;
for (const item of items) {
if (item.quantity == "2") sum += item.price;
}
return summ;
}
Avec la configuration ci-dessus, cinq problèmes sont signalés. total n’est jamais utilisée. sum est mise à jour, mais sa valeur n’est jamais utilisée en dehors de ces mises à jour. unused n’est pas utilisée. La règle eqeqeq signale ==, et no-undef repère la faute de frappe summ. L’appel de cette fonction échouerait sur ce nom non défini, mais le linter peut le trouver sans l’appeler.
Commence par le nom et l’emplacement de la règle dans le rapport. Corrige le comportement voulu, puis relance la commande. npm run lint -- --fix demande des corrections automatiques sûres lorsque le projet possède un script lint. L’outil ne peut pas décider de la bonne logique métier pour chaque avertissement. Ne désactive une règle sur une ligne que si tu as une raison, et indique cette raison à côté du commentaire.
Confie la mise en forme à un seul formateur
npm install --save-dev --save-exact prettier
npx prettier . --write
npx prettier . --check
--write modifie les fichiers ; --check signale les différences de mise en forme sans les modifier. Avec les réglages par défaut, const point={x:1,y:2} devient const point = { x: 1, y: 2 };. Les options comme "singleQuote": true se placent dans .prettierrc.
| Outil | Question principale |
|---|---|
| ESLint | Ce code contient-il une construction signalée par une règle ? |
| Prettier | Le code respecte-t-il la mise en forme choisie ? |
| Exécuteur de tests | Le code produit-il le comportement attendu ? |
Les règles recommandées d’ESLint n’imposent pas la mise en forme de Prettier : cette configuration évite donc les conflits de mise en forme. Si une autre configuration ESLint ajoute des règles de style incompatibles, eslint-config-prettier les désactive. L’exécution de deux formateurs concurrents peut modifier les fichiers dans un sens puis dans l’autre.
Ajoute des scripts lint et format pour faciliter la répétition des vérifications. Après configuration, les extensions de l’éditeur peuvent afficher des diagnostics pendant la saisie et mettre en forme à l’enregistrement. Les deux outils ignorent node_modules par défaut ; ajoute les résultats générés comme dist aux paramètres d’exclusion appropriés. Une fonction qui ne déclenche aucun avertissement du linter peut quand même calculer le périmètre alors qu’elle devrait calculer l’aire ; le comportement nécessite des tests.
Points clés
- Un linter trouve les problèmes du code source prévus par sa configuration sans exécuter le programme.
- Dans ESLint, le niveau erreur fait échouer la commande ; les avertissements ne la font pas échouer par défaut.
- Prettier modifie la mise en forme avec
--writeet la vérifie avec--check. - Utilise des règles compatibles pour le linter et la mise en forme, ainsi que des tests pour le comportement.
4.2 Développe localement et prépare les fichiers pour le navigateur
Un navigateur peut charger directement quelques modules ES, mais les projets plus importants utilisent aussi des paquets installés et des fichiers qui nécessitent une transformation. Un serveur de développement prépare les fichiers pendant que tu les modifies. Un bundler suit les dépendances pour produire des fichiers adaptés à la publication.
Parcours le cycle Vite
npm create vite@latest shape-lab
cd shape-lab
npm install
npm run dev
npm run build
npm run preview
Choisis le modèle JavaScript Vanilla pour ce projet. Le générateur fournit des scripts de développement et de compilation. npm run dev affiche une adresse locale, souvent http://localhost:5173. Ouvre cette adresse pendant les modifications. Dans Vite, index.html est le point d’entrée et charge généralement /src/main.js avec un script de module.
Le serveur de développement résout les noms de paquets et transforme les fichiers selon les besoins. L’enregistrement d’un fichier déclenche une mise à jour. Le remplacement de modules à chaud, ou HMR, remplace un module modifié dans la page en cours d’exécution ; les mises à jour prises en charge peuvent préserver l’état au lieu d’imposer un rechargement complet.
npm run build écrit les résultats optimisés dans dist. npm run preview sert ces résultats localement afin que tu puisses vérifier la compilation de production réelle. La prévisualisation nécessite une compilation existante et n’en crée pas une nouvelle. Modifie les fichiers sources, car une nouvelle compilation remplace les résultats générés.
Comprends les changements apportés pour la production
| Fonction de compilation | Rôle |
|---|---|
| Regroupement | Combiner les modules en un plus petit nombre de fichiers pour le navigateur |
| Tree shaking | Supprimer le code dont la compilation peut prouver qu’il est inutilisé |
| Minification | Réduire la taille en supprimant la mise en forme et en raccourcissant les noms |
| Empreintes du contenu | Changer les noms des ressources lorsque leur contenu change, pour faciliter la mise en cache |
| Fractionnement du code | Garder une partie du code dans des fragments distincts qui peuvent être chargés plus tard |
Par exemple, un module de fonctions utilitaires peut exporter à la fois formatArea et formatVolume. Si seule formatArea est utilisée et que la suppression de l’autre code est sûre, le tree shaking peut omettre formatVolume. Les importations et exportations statiques rendent les dépendances visibles. Les effets de bord d’un module au niveau supérieur peuvent devoir être conservés même si aucune de ses exportations n’est utilisée.
Un import() dynamique donne à l’outil de compilation un point de séparation pour un chargement ultérieur. Les outils de compilation peuvent créer des fragments distincts à cet endroit ; leur organisation dépend de la compilation, donc un module source ne devient pas toujours exactement un fichier de sortie.
Un nom comme assets/index-4f2a9c1b.js change avec son contenu, ce qui permet de conserver longtemps les fichiers dans le cache du navigateur sans servir un ancien fichier après une mise à jour. Une carte des sources relie les emplacements générés aux lignes du code source d’origine, ce qui facilite les traces de pile et le débogage lorsque les cartes sont disponibles.
Inclus les ressources et la configuration publique
Vite traite aussi les ressources : import "./style.css"; inclut des styles et import logoUrl from "./logo.png"; fournit l’URL finale de l’image. La compilation peut mettre ces références à jour pour les faire correspondre aux noms de fichiers générés.
Lis les informations de mode avec import.meta.env.MODE. Ses valeurs par défaut sont "development" pour le développement et "production" pour une compilation ; un mode personnalisé peut utiliser un autre nom. Les valeurs d’environnement personnalisées commençant par VITE_ sont exposées au code du navigateur par défaut. Tout ce qui est envoyé au navigateur est public : garde les clés secrètes et les mots de passe sur un serveur, hors du code du navigateur et des variables d’environnement exposées.
Ne vérifie pas uniquement la page de développement. Compile et prévisualise avant la publication pour repérer les problèmes dans les résultats transformés et les chemins des ressources. Ouvrir dist/index.html directement depuis le disque ne reproduit pas la mise à disposition du site sur HTTP. Une petite page avec quelques modules simples peut n’avoir besoin que d’un serveur statique, sans outil de compilation complet.
Points clés
- Un serveur de développement résout et transforme les fichiers pendant les modifications ; une compilation de production crée les fichiers à publier.
- Le HMR peut remplacer le code modifié tout en conservant l’état de la page lorsque la mise à jour le permet.
- Le tree shaking ne supprime du code que lorsque la compilation peut l’omettre sans risque.
- Les empreintes du contenu facilitent la mise en cache et les cartes des sources relient les résultats au code source.
- Compile d’abord, puis prévisualise localement les résultats générés.
- Les bundles du navigateur et les valeurs d’environnement exposées sont publics.
4.3 Teste le comportement avec des assertions
Un test automatisé exécute du code avec des entrées connues et vérifie le résultat à l’aide d’assertions. Un test unitaire porte sur un petit élément, comme une fonction ou une méthode. L’exécuteur trouve les tests, les exécute, signale les échecs et termine avec un code indiquant un échec lorsqu’un test échoue.
Prépare, agis, vérifie
Prépare les entrées, agis en appelant le code et vérifie le résultat attendu. Un nom de test utile décrit le comportement. Pour une classe de rectangle dont le constructeur rejette un côté négatif :
import { test } from "node:test";
import assert from "node:assert/strict";
import { Rectangle } from "./rectangle.js";
test("area multiplies width by height", () => {
assert.equal(new Rectangle(3, 4).area(), 12);
});
test("rejects a negative width", () => {
assert.throws(() => new Rectangle(-1, 4), RangeError);
});
La première assertion vérifie que le résultat est de 12 cm², une valeur connue indépendamment du code. La seconde passe une fonction à assert.throws pour que l’assertion puisse observer l’erreur. La commande node --test trouve les fichiers dont le nom ressemble à rectangle.test.js ; le script de paquet "test": "node --test" permet d’utiliser npm test.
Le mode d’assertion strict utilise Object.is pour assert.equal, sans conversion implicite. assert.deepEqual compare le contenu des objets et des tableaux. Deux tableaux distincts contenant les mêmes valeurs ne sont pas le même objet : une comparaison stricte d’identité ne convient donc pas pour vérifier l’égalité de leurs contenus.
Utilise les mêmes principes dans Vitest
import { describe, it, expect } from "vitest";
import { Rectangle } from "./rectangle.js";
describe("Rectangle", () => {
it("computes the area", () => {
expect(new Rectangle(3, 4).area()).toBe(12);
});
it("rejects a negative width", () => {
expect(() => new Rectangle(-1, 4)).toThrow(RangeError);
});
});
Vitest utilise describe pour regrouper les tests et it pour les tests individuels. Exécute npx vitest pour surveiller les modifications ou npx vitest run pour une seule exécution. Jest propose un style similaire. Utilise toEqual pour les contenus et toBe pour l’identité ou les valeurs primitives.
Inclus les bornes et les limites numériques
Teste les bornes exactes d’un intervalle autorisé ainsi que les entrées juste à l’extérieur. Pour un thermostat qui accepte de 10 à 30 °C, il faut des tests qui acceptent 10 et 30, ainsi que des tests qui rejettent 9,5 et 30,5. Ajoute des vérifications distinctes pour les valeurs non numériques et non finies si l’interface les rejette. Les tests ne doivent pas dépendre d’un objet modifié par un test précédent ni de leur ordre d’exécution.
Le calcul en virgule flottante peut ne pas correspondre exactement à un nombre décimal écrit : 0.1 + 0.2 vaut 0.30000000000000004. Pour utiliser une tolérance adaptée, écris assert.ok(Math.abs(actual - expected) < 1e-9). Un cercle de rayon 3 a une aire d’environ 28,274333882 cm² ; toBeCloseTo(28.27, 2) de Vitest vérifie que la valeur est proche avec cette précision décimale. Choisis la tolérance selon ce que le test doit établir.
Tire des enseignements d’un échec
Lis le comportement nommé, les valeurs attendue et obtenue, ainsi que l’emplacement dans le code source. Détermine si l’erreur vient de l’implémentation ou du résultat attendu par le test. Dans le développement piloté par les tests, écris un test qui échoue, implémente juste assez pour le faire réussir, puis améliore le code en gardant les tests au vert.
Exporte la logique depuis les modules et passe les entrées comme arguments. Un gestionnaire d’événements peut alors recueillir les données de la page et appeler cette logique, au lieu de mêler les calculs aux opérations sur le DOM. Le code dépendant du DOM peut utiliser un navigateur simulé comme jsdom. Attends les opérations asynchrones dans un test asynchrone pour que le test ne se termine pas avant ses vérifications. Un test qui se contente de répéter la formule de l’implémentation peut répéter son bogue ; utilise le comportement attendu et des exemples indépendants.
Points clés
- Les tests exécutent le code et utilisent des assertions pour vérifier un comportement attendu établi indépendamment.
- Prépare les entrées, agis et vérifie un comportement, avec un nom de test utile.
- Compare les contenus des tableaux ou objets distincts dont les contenus sont égaux.
- Teste les bornes et les entrées invalides, et utilise des tolérances adaptées aux calculs en virgule flottante.
- Attends les opérations asynchrones et garde les tests indépendants de leur ordre d’exécution.
5Situe les types et les frameworks dans la méthode de travail
TypeScript et les frameworks front-end s’appuient sur les outils JavaScript déjà présentés. Les types aident un éditeur et un vérificateur à trouver les valeurs incompatibles avant l’exécution. Les composants aident à organiser les interfaces qui partagent et modifient des données. Aucun des deux ne dispense de comprendre les modules, les dépendances ou les tests. Utilise les scripts du projet pour voir comment ces éléments coopèrent, puis ajoute des outils lorsque le travail leur donne un rôle clair.
5.1 Comprends ce que TypeScript vérifie
TypeScript ajoute à JavaScript une syntaxe de types statiques. Un type décrit le genre de valeur attendu par le code. Le vérificateur utilise ces descriptions avant l’exécution du programme, ce qui peut révéler des erreurs avant qu’un utilisateur ne les rencontre.
Vérifie un appel avant son exécution
function area(width: number, height: number): number {
return width * height;
}
area(3, "4"); // type error: the second argument is a string
Les deux paramètres doivent être des nombres. La valeur écrite "4" est une chaîne : le vérificateur signale donc l’incompatibilité. JavaScript ordinaire convertirait implicitement cette chaîne dans une multiplication : 3 * "4" donne 12. Avec une addition, 3 + "4" donne "34". Une erreur de type indique une incompatibilité dans les entrées prévues, même si JavaScript pourrait exécuter l’expression.
Une interface décrit la structure d’un objet :
interface Shape {
name: string;
area(): number;
}
Une classe déclarée avec implements Shape doit fournir les propriétés et méthodes indiquées avec des types compatibles. Le vérificateur peut aussi détecter les propriétés mal orthographiées, les nombres incorrects d’arguments et les valeurs potentiellement undefined lorsque la vérification stricte est activée.
Distingue la vérification de la suppression des types
| Outil ou action | Rôle |
|---|---|
tsc | Vérifie les types et produit normalement du JavaScript |
tsc --noEmit | Vérifie les types sans écrire de fichiers en sortie |
| Transformation TypeScript de Vite | Supprime la syntaxe de type pour l’exécution |
| Effacement des types de Node.js | Exécute le TypeScript pris en charge dont les types peuvent être effacés, après leur suppression |
Les réglages du projet TypeScript se trouvent dans tsconfig.json. Une compilation Vite ne démontre pas à elle seule que tous les types sont corrects. Les projets exécutent généralement tsc --noEmit séparément. Les versions actuelles de Node.js peuvent exécuter directement les fichiers .ts pris en charge, mais leur effacement des types ne vérifie pas non plus ces derniers.
Garde des vérifications à l’exécution aux points d’entrée des données
Les annotations de type sont supprimées avant l’exécution. Déclarer le type attendu des données provenant de fetch() ou de JSON.parse() ne vérifie pas leur valeur réelle. Vérifie les données externes dans le code en cours d’exécution avant de t’y fier. Un serveur peut envoyer une chaîne même si une déclaration de type indique qu’un champ est numérique.
Les mots-clés private et readonly de TypeScript sont des restrictions du vérificateur ; ils n’assurent pas la confidentialité à l’exécution offerte par # en JavaScript. Les champs privés JavaScript restent protégés pendant l’exécution du programme. TypeScript n’accélère pas non plus un programme et ne prouve pas que ses calculs sont corrects.
Le type any désactive des vérifications utiles pour une valeur. Il peut faire taire une erreur tout en laissant subsister l’erreur d’origine. Corriger une hypothèse erronée ou ajouter une véritable vérification des données donne de meilleures informations au vérificateur.
Adopte-le par étapes abordables
Un fichier JavaScript peut commencer par // @ts-check et utiliser JSDoc, par exemple /** @param {number} width */, pour bénéficier de vérifications dans les éditeurs compatibles sans devenir un fichier .ts. Un projet peut ensuite renommer les fichiers un par un. Les bibliothèques peuvent fournir des définitions de types .d.ts ; les définitions communautaires sont souvent distribuées sous @types/....
Les projets plus importants et durables gagnent des interfaces plus claires, des remaniements plus sûrs et une meilleure complétion dans l’éditeur. Un petit script ne justifie peut-être pas encore une étape de compilation et une syntaxe supplémentaires. Les tests restent utiles dans les deux cas : une fonction d’aire correctement typée peut quand même renvoyer le mauvais résultat numérique.
Points clés
- TypeScript vérifie les types de valeurs attendus avant l’exécution.
- La suppression de la syntaxe de type est distincte de la vérification des types.
- Les déclarations de type ne valident pas les données externes pendant l’exécution du programme.
- La confidentialité assurée par
#en JavaScript est imposée à l’exécution ;privatede TypeScript est vérifié statiquement. - La vérification JSDoc permet une adoption progressive avant la conversion des fichiers en TypeScript.
5.2 Combine les outils selon les besoins du projet
Une bibliothèque fournit du code que ton programme appelle. Un framework fournit une structure d’application et appelle ton code aux moments appropriés. Ce changement de contrôle aide à coordonner des interfaces plus importantes, mais repose toujours sur JavaScript, les modules et une chaîne d’outils de projet.
Organise une interface en composants
React, Vue, Svelte et Angular organisent les interfaces utilisateur avec des composants : des éléments réutilisables comme une barre de navigation ou une tâche à faire. Un composant combine du balisage et un comportement, souvent avec des styles. Lorsque son état change, le système d’interface utilisateur coordonne les mises à jour correspondantes de la page.
Les méta-frameworks comme Next.js, Nuxt, SvelteKit et Astro ajoutent une infrastructure plus complète, comprenant le routage, le rendu sur le serveur et la prise en charge de la compilation. Leurs fonctionnalités exactes diffèrent. Connaître les fonctions JavaScript, les événements, le code asynchrone et les limites entre modules aide à apprendre chacun d’eux.
Pour une page avec quelques fonctionnalités interactives indépendantes, du JavaScript ordinaire peut rester clair. Un framework devient utile lorsque de nombreuses vues doivent partager des données changeantes. Choisis-le pour ce besoin au lieu de supposer que chaque page nécessite la chaîne d’outils la plus complète.
Suis la coopération des outils
Les paquets des frameworks sont installés avec npm et importés comme modules. Les outils de compilation transforment des syntaxes comme JSX ou des fichiers propres à un framework. TypeScript peut vérifier les interfaces entre les parties. La vérification par un linter, la mise en forme et les tests gardent leurs rôles distincts quel que soit le choix d’interface utilisateur.
Commence un projet simple et ajoute les outils utilisés par l’équipe :
npm create vite@latest shape-lab
cd shape-lab
npm install
npm install --save-dev eslint @eslint/js globals prettier vitest
Un modèle Vite fournit les outils de développement et de compilation. L’installation de paquets supplémentaires ne les configure pas : ajoute la configuration ESLint, crée des tests et configure les extensions de l’éditeur pour vérifier et mettre en forme le code à l’enregistrement. Un champ de scripts peut fournir ces tâches :
{
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint .",
"format": "prettier . --write",
"test": "vitest run"
}
Ces entrées se placent dans "scripts" dans package.json. Pendant les modifications, exécute npm run dev ; l’éditeur peut vérifier et mettre en forme les fichiers à mesure qu’ils changent. Avant de partager le travail, exécute npm test. Avant la publication, exécute npm run build et npm run preview. Le dossier dist compilé d’un site statique peut ensuite être envoyé à un hébergeur statique adapté.
Trouve le fichier qui contrôle une tâche
| Fichier ou dossier | Rôle |
|---|---|
package.json | Commandes et plages de versions des dépendances directes |
package-lock.json | Arbre exact des dépendances |
node_modules/ | Paquets installés générés |
eslint.config.js | Règles du linter et environnements |
.prettierrc | Options de mise en forme |
vite.config.js | Options de développement et de compilation |
tsconfig.json | Réglages de vérification et de compilation TypeScript |
dist/ | Résultats de compilation générés |
Commence l’étude d’un projet inconnu par package.json : ses scripts révèlent comment le projet s’exécute et ses listes de dépendances indiquent les outils nécessaires à ces scripts. Les dossiers générés peuvent être recréés ; les réglages et les sources expliquent comment les recréer.
Apprends les nouveaux outils un par un afin que la cause d’une erreur reste reconnaissable. Utilise la documentation officielle de la version installée lorsque les commandes ou les réglages par défaut diffèrent, car les versions des outils et des frameworks changent plus souvent que la syntaxe de base de JavaScript.
Points clés
- Une bibliothèque est appelée par ton code ; un framework appelle ton code dans sa structure.
- Les composants coordonnent des éléments réutilisables de l’interface utilisateur et un état qui change.
- Les projets utilisant un framework dépendent toujours des modules, des paquets, des compilations et des vérifications.
- Les modèles et l’installation de paquets ne configurent pas automatiquement tous les outils supplémentaires.
- Lis d’abord les scripts du paquet pour découvrir la méthode de travail d’un projet.
- Ajoute des outils pour répondre à un besoin concret et consulte la documentation de la version installée.
Classes et modules JavaScript : exemples
1Construis une petite famille d’objets
18 étapes
- 1IntroductionCommence par des objets qui ont besoin de comportements
Un programme peut stocker de nombreux rectangles, mais chacun a aussi besoin d’opérations comme l’aire et le périmètre. Une classe permet à ces objets de partager les opérations tout en gardant leurs dimensions distinctes.
Suis l’objet depuis sa création jusqu’à un appel de méthode. Ajoute ensuite un état protégé, distingue les valeurs au niveau de la classe et spécialise le comportement par héritage. Enfin, examine la chaîne de prototypes pour voir où se trouvent les méthodes partagées.
- 2ExempleInitialise une instance
Le constructeur stocke des valeurs sur le nouvel objet. Ici, les dimensions sont en centimètres :
class Rectangle { constructor(width, height) { this.width = width; this.height = height; } area() { return this.width * this.height; } } const a = new Rectangle(3, 4);newcréeaet exécute le constructeur avecthisqui le désigne. Les deux affectations donnent àasa propre largeur et sa propre hauteur. - 3ExempleAppelle un comportement partagé avec des données distinctes
Ajoute
const b = new Rectangle(5, 2);. Les deux objets peuvent appelerarea, mais chaque appel lit les dimensions de l’objet receveur.a.area()multiplie 3 par 4 et renvoie 12 cm².b.area()multiplie 5 par 2 et renvoie 10 cm². L’affectationb.width = 8ne modifie queb: son aire suivante est donc de 16 cm², tandis que celle deareste à 12 cm².La méthode n’a pas besoin d’une copie distincte de son code pour chaque rectangle. Les données qu’elle lit proviennent de
this. - 4ConseilConserve l’objet dans une fonction de rappel
Extraire une méthode d’un objet ne lui attache pas définitivement son objet receveur :
const calculate = a.area; // calculate(); // this is undefined const calculateSafely = () => a.area();Les méthodes de classe s’exécutent en mode strict. L’appel détaché n’a pas d’objet avant le point, donc la lecture de
this.widthéchoue. La fonction fléchée enveloppante conserve l’appel de méthode d’origine.a.area.bind(a)est une autre façon de produire une fonction de rappel liée àa. - 5DéfiPrévois un deuxième appel
Un nouveau
Rectangle(6, 2)est affecté àtile. L’instructiontile.width = 7s’exécute. Que renvoietile.area()et pourquoi cela ne modifie-t-il pas un autre rectangle créé auparavant ?---reveal---
Elle renvoie 14 cm² : la méthode lit la largeur actuelle de l’objet receveur, 7 cm, et sa hauteur, 2 cm. Chaque instance stocke ses propres dimensions : modifier
tile.widthne met donc pas à jour une autre instance. Le partage d’une méthode ne rend pas les champs d’instance partagés. - 6ExplicationStocke l’état derrière un accesseur en écriture
Un thermostat peut exposer
targettout en stockant#targetde façon privée. Un accesseur en lecture lit la valeur privée ; un accesseur en écriture vérifie une modification demandée.L’accesseur en écriture rejette les entrées non finies ou non numériques et les valeurs hors de l’intervalle de 10 à 30 °C avant d’effectuer l’affectation
this.#target = value. Cet ordre compte : lever une exception avant l’affectation préserve la dernière consigne valide. Le nom de stockage distinct évite aussi que l’accesseur en écriture se rappelle sans cesse. - 7ExempleSuis une modification acceptée et une modification rejetée
Pars d’une consigne de thermostat de 20 °C. L’affectation de 22 passe les deux vérifications : l’accesseur en écriture stocke donc 22. L’affectation de 35 lève ensuite une
RangeError; la valeur stockée reste à 22.Number.isFinite(value)rejette aussi"22",NaNet l’infini. Les seules comparaisons aux bornes ne rejetteraient pas tous les types ou valeurs invalides. Le stockage privé contrôle l’accès direct, tandis que la validation contrôle ce que l’interface publique accepte. - 8Point cléChoisis entre stockage sur la classe et sur l’instance
Un champ déclaré avec
staticappartient à la classe. Lis la borne inférieure du thermostat avecThermostat.MIN, tandis quet.targetlit l’état d’une instance.Un compteur statique peut suivre le nombre d’objets construits. Un champ ordinaire est au contraire initialisé séparément sur chaque nouvel objet. Dans une méthode statique,
thisest la classe qui reçoit l’appel : il ne fournit donc pas la consigne ou les dimensions d’une instance. - 9DéfiUtilise l’interface publique
Une sous-classe de
Thermostatdoit augmenter sa consigne de 20 à 24 °C. La classe parente déclare#targetet un accesseur en écriture publictargetqui valide les valeurs. Comment la sous-classe doit-elle demander la modification, et que produirait un accès direct avecthis.#target?---reveal---
Effectue l’affectation
this.target = 24pour que l’accesseur en écriture hérité valide et stocke la valeur. La sous-classe ne peut pas nommer directement le champ privé de la classe parente ; cette utilisation de#targetest une erreur de syntaxe. Les accesseurs hérités s’exécutent toujours dans la classe qui a déclaré l’état privé. - 10ExempleDonne une classe parente à des classes apparentées
Un constructeur
Shapestocke un nom et sa méthodedescribe()appellethis.area(). Un cercle spécialise le calcul :class Circle extends Shape { constructor(radius) { super("Circle"); this.radius = radius; } area() { return Math.PI * this.radius ** 2; } }superinitialise la partie parente avant l’utilisation dethis.radius. Un cercle est une forme : la description héritée convient donc à son rôle. - 11ExempleLaisse l’objet receveur choisir le calcul
Avec un rayon de 2 cm,
new Circle(2).describe()renvoie"Circle with area 12.57 cm²". La méthode de description est héritée, mais son appel àthis.area()trouve la méthode redéfinie du cercle.Un rectangle peut hériter de cette même description et fournir une multiplication comme méthode d’aire. Une boucle qui appelle
describe()sur les deux objets n’a pas besoin de branches distinctes pour le cercle et le rectangle. Cette interface commune avec des comportements différents est le polymorphisme. - 12ExplicationÉtends une méthode de la classe parente
Un carré peut appeler
super(side, side)dans son constructeur pour donner des dimensions égales à sa classe parente rectangle. Une description redéfinie peut ensuite appelersuper.describe()et ajouter la longueur du côté.super(...)dans le constructeur etsuper.describe()dans une méthode ont des rôles différents : le premier initialise la classe parente, le second appelle une méthode parente. Une sous-classe sans constructeur propre transmet automatiquement les arguments. - 13DéfiChoisis un modèle adapté
Un objet
Reportdoit envoyer des messages par l’intermédiaire d’unNotifier. Les rapports ne sont pas des systèmes de notification. Quelle relation faut-il établir entre les objets et comment le rapport doit-il demander une notification ?---reveal---
Utilise la composition : donne au rapport une propriété contenant le système de notification, puis appelle une méthode comme
this.notifier.send(message). C’est une relation « possède un ». ÉtendreNotifierreviendrait à affirmer que chaque rapport est lui-même un système de notification simplement pour réutiliser son code, ce qui ne correspond pas au modèle. - 14ExplicationTrouve une méthode dans la chaîne
Les propriétés propres d’un cercle contiennent des données comme son rayon. Lorsqu’il ne possède pas de propriété propre
describe, JavaScript recherche dansCircle.prototype, puis dansShape.prototype, où cette méthode est stockée.La chaîne ordinaire continue par
Object.prototypejusqu’ànull. Si aucun objet de la chaîne ne possède une propriété demandée, sa lecture donneundefined.instanceof Shaperéussit normalement parce queShape.prototypeapparaît dans la chaîne de l’instance. - 15ExempleMasque un paramètre hérité
Crée
settingsavecObject.create(defaults), oùdefaults.fontSizevaut 16. Effectue l’affectationsettings.fontSize = 18.L’affectation crée une propriété de données propre qui masque la propriété héritée. La valeur par défaut reste à 16. Supprimer
settings.fontSizeretire le masquage : la lecture suivante retrouve donc 16 sur le prototype. Ce suivi suppose une propriété de données ordinaire ; un accesseur en écriture hérité peut toutefois traiter une affectation à sa place. - 16DéfiVérifie séparément si une propriété est propre et si elle est accessible
Un objet
panelhérite detheme: "light"d’un prototype ordinaire et ne possède pas sa propre propriététheme. Que produisentpanel.theme,Object.hasOwn(panel, "theme")et"theme" in panel?---reveal---
Ils produisent
"light",falseettrue. La lecture etinpeuvent trouver des propriétés héritées, tandis queObject.hasOwnvérifie uniquement si la propriété est stockée surpanellui-même. Une lecture réussie ne prouve pas que la propriété est propre.Object.keys(panel)omet aussi cette clé héritée. - 17DéfiRelie héritage, confidentialité et recherche
Sensordéclare#value, un accesseur en lecture publicvalueet une méthodedescribe()qui litthis.value.IndoorSensor extends Sensorn’ajoute aucun membre. Pourquoi un capteur intérieur peut-il appelerdescribe()alors que le corps de sa classe ne peut pas nommer#value?---reveal---
La recherche trouve la méthode héritée, qui s’exécute avec le capteur intérieur comme
this. L’accesseur en lecture déclaré dansSensorpeut lire son propre champ privé sur cette instance. L’héritage fournit le comportement public sans donner à la sous-classe un accès direct au nom privé de la classe parente. - 18RécapitulatifExamine un objet dans un ordre utile
Lorsque tu suis une instance de classe, demande-toi où ses données sont stockées, quel objet reçoit l’appel de méthode et où la recherche trouve la méthode. Si une modification passe par un accesseur en écriture, suis la validation avant l’affectation.
Pour une hiérarchie inconnue, examine
extendset les arguments du constructeur avant de suivre les méthodes redéfinies. Vérifie si les propriétés sont propres lorsque tu as besoin des clés stockées, et suis les prototypes lorsque tu dois expliquer un comportement hérité.
2Relie un programme entre ses modules
13 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionSuis un graphe de dépendances
- 2ExempleExporte le calcul
- 3ExempleExporte et utilise la classe
- 4DéfiLis une exportation modifiée
- 5ExempleLance les fichiers dans un navigateur
- 6ExplicationRésous un nom de paquet
- 7DéfiChoisis le système de chargement de Node.js
- 8ExempleReporte le chargement d’un graphique facultatif
- 9Point cléDétermine ce qui attend l’initialisation
- 10DéfiRomps un cycle inutile
- 11ConseilÉvite d’effectuer du travail à l’importation
- 12DéfiCombine le chargement avec une interface claire
- 13RécapitulatifSuis une erreur de module étape par étape
3Recrée un projet npm sur un autre ordinateur
11 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionRends le projet reproductible
- 2ExempleCrée la description du paquet
- 3ExempleLis la tâche avant de l’exécuter
- 4DéfiIdentifie un changement de version
- 5ExplicationEnregistre séparément le code et les outils
- 6ExempleLis ce qu’une plage autorise
- 7ExplicationInstalle l’arbre enregistré
- 8DéfiInterprète une installation propre bloquée
- 9ConseilPrévois une mise à jour réfléchie
- 10DéfiCombine une plage de versions et une compilation propre
- 11RécapitulatifLis ensemble les deux enregistrements du paquet
4Vérifie un projet de la modification à la compilation
15 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionPose une question différente à chaque outil
- 2ExplicationConfigure un rapport de linter utile
- 3ExempleTrouve une faute de frappe avant l’exécution
- 4DéfiChoisis une vérification de mise en forme
- 5ExempleLance le développement, la compilation et la prévisualisation
- 6ExempleSuis une optimisation de compilation
- 7LienRelie les résultats aux sources
- 8DéfiChoisis ce qu’il faut reconstruire
- 9ExempleÉcris un résultat attendu indépendant
- 10Point cléCompare correctement les contenus et l’identité
- 11DéfiTeste la borne exacte
- 12ConseilAttends un test asynchrone
- 13ExempleUtilise une tolérance numérique adaptée
- 14DéfiExamine une compilation réussie avec un résultat incorrect
- 15RécapitulatifLis les échecs comme des éléments d’enquête
5Intègre les types et les composants dans un projet fonctionnel
10 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionAjoute un outil pour un besoin précis
- 2ExempleRepère un appel incompatible
- 3ExplicationDistingue la transformation de la vérification
- 4DéfiVérifie les données qui arrivent à l’exécution
- 5ExplicationSitue un composant dans la méthode de travail
- 6ExempleLis le menu des tâches du projet
- 7DéfiDétermine quand un framework est utile
- 8DéfiTrouve la vérification manquante
- 9ConseilAdopte progressivement la vérification
- 10RécapitulatifSuis chaque outil jusqu’aux éléments qu’il fournit
Classes et modules JavaScript : QCM
Série 1 : Lis des classes, des importations et des tâches de projet10 Questions Pratiques
Question 1 sur 10
LIMIT par son nom. Comment une seule déclaration peut-elle importer la fonction sous le nom calculate et la constante sous son nom exporté ?Afficher la réponseMasquer la réponse
{ calculate, LIMIT } revient à demander deux exportations nommées, mais la fonction a été fournie comme exportation par défaut. Le nom local calculate est choisi par le fichier importateur et n’a pas besoin de correspondre au nom de déclaration de la fonction.Question 2 sur 10
static created = 8;. Son instance est affectée à batch et la classe se nomme Batch. Comment lit-on le compteur stocké ?Afficher la réponseMasquer la réponse
Batch.created. La lecture de batch.created recherche dans l’instance et sa chaîne de prototypes, où ce champ statique n’a pas été placé. Statique ne signifie pas qu’un champ est copié sur chaque objet.Question 3 sur 10
Pourquoi ce constructeur échoue-t-il et quel ordre corrige le problème ?
class Item {
constructor(code) { this.code = code; }
}
class Box extends Item {
constructor(code) {
this.open = false;
super(code);
}
}
new Box("K7");
Afficher la réponseMasquer la réponse
this avant que le constructeur parent ait initialisé l’instance avec super(code). Déplace cet appel avant l’affectation de open. Affecter d’abord un autre champ poserait le même problème, car cela accéderait encore à this trop tôt.Question 4 sur 10
export default class Badge {}. Quelle importation lie cette classe au nom local Tag ?Afficher la réponseMasquer la réponse
Tag. Importer { Badge as Tag } demande au contraire une exportation nommée Badge, que ce module ne fournit pas. Une importation dans un espace de noms lierait un objet contenant l’exportation par défaut, pas la classe elle-même.Question 5 sur 10
import, mais sa balise script n’a pas d’attribut type. Quelle modification HTML en fait un module ES ?Afficher la réponseMasquer la réponse
type="module" indique au navigateur d’utiliser la syntaxe et les règles de chargement des modules ES. Ajouter uniquement defer modifie le moment d’exécution d’un script classique, mais n’y rend pas les déclarations d’importation valides. Le module d’entrée peut alors charger automatiquement ses dépendances lorsque les fichiers sont correctement servis.Question 6 sur 10
"scripts": { "check": "eslint ." }. Quelle commande exécute cette tâche nommée ?Afficher la réponseMasquer la réponse
npm run : cette tâche s’exécute donc avec npm run check. npm check ne devient pas une forme courte simplement parce qu’un script porte ce nom ; les formes courantes npm test et npm start sont des cas particuliers. Le script exécute la commande ESLint enregistrée.Question 7 sur 10
Afficher la réponseMasquer la réponse
devDependencies, où il est normalement ajouté avec npm install -D. La section des scripts peut contenir une commande qui l’exécute, mais cela n’enregistre pas la dépendance au paquet. Le code de bibliothèque nécessaire à l’exécution appartient à dependencies.Question 8 sur 10
Afficher la réponseMasquer la réponse
Question 9 sur 10
"a" et "b". Comment un test Vitest doit-il vérifier ce contenu par rapport à un littéral de tableau distinct ?Afficher la réponseMasquer la réponse
toEqual vérifie le contenu du tableau, y compris l’ordre de ses valeurs. toBe et les expressions d’identité vérifient si les deux opérandes sont le même objet ; un littéral de tableau distinct crée un autre objet. Pour vérifier l’égalité des contenus, il faut donc la comparaison profonde présentée.Question 10 sur 10
Afficher la réponseMasquer la réponse
Série 2 : Utilise les modules et des réglages de projet reproductibles10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Que se passe-t-il à la dernière ligne ?
class Meter {
value = 2;
read() { return this.value; }
}
const meter = new Meter();
const read = meter.read;
read();
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
Quel est le dernier résultat ?
const base = { unit: "cm" };
const sample = Object.create(base);
Object.hasOwn(sample, "unit");
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
Que renvoie l’expression finale ?
class Label {
constructor(text) { this.text = text; }
length() { return this.text.length; }
}
new Label("Orbit").length();
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
import("./help.js") ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
~6.4.2. Quelle est la version stable autorisée la plus récente parmi 6.4.9, 6.5.0, 7.0.0 et 6.4.1 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
loadItems() sans l’attendre. La fonction de rappel du test se termine ; la promesse est ensuite tenue avec un nouveau tableau. L’assertion prévue utilise toBe(["x"]). Que faut-il changer pour que le test vérifie de manière fiable le contenu obtenu par la promesse ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
.ts s’exécute dans Node.js grâce à l’effacement des types pris en charge. Pourquoi cette exécution réussie ne prouve-t-elle pas que tous les types déclarés sont corrects ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 3 : Suis l’état, les dépendances et le comportement de la compilation10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
"8", quelle valeur reste et pourquoi ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
Quelles sont les valeurs lues à la fin ?
const base = { value: 4, read() { return this.value; } };
const x = Object.create(base);
const y = Object.create(x);
x.value = 7;
y.value = 10;
delete y.value;
[y.read(), Object.hasOwn(y, "read"), base.value];
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
Quelles sont les deux valeurs finales ?
const defaults = { size: 11 };
const item = Object.create(defaults);
item.size = 15;
delete item.size;
[item.size, defaults.size];
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
"tiny-calendar". Le paquet est installé localement, mais le navigateur signale un spécificateur non résolu. Quel type de configuration manquante explique ce problème ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
main.js importe statiquement config.js, qui attend une réponse réseau avant d’exporter des paramètres. Un point d’entrée indépendant clock.js n’importe aucun des deux fichiers. Plus tard, le gestionnaire asynchrone de main exécute const plugin = await import("./plugin.js") ; ce module n’exporte que la classe Plugin par défaut. Quelle réponse décrit correctement l’attente et la construction ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
^8.1.0 et verrouillent une dépendance à la version 8.1.3. Une compilation propre exécute npm ci après la publication de 8.7.0. Quelle version est installée ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 4 : Diagnostique les problèmes de classes et de projets10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Quelles sont les valeurs lues à la fin ?
class Counter {
count = 1;
add() { this.count += 2; }
}
const a = new Counter();
const b = new Counter();
const addA = a.add.bind(a);
b.count = 10;
addA();
const callB = () => b.add();
callB();
[a.count, b.count];
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
#level et fournit un accesseur en lecture public sur son prototype. Elle ne définit pas de méthode toJSON. Que produit JSON.stringify(new Gauge()) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
class Photo extends Asset {} est déclarée sans constructeur. Asset stocke l’argument transmis à son constructeur. Que devient l’argument dans new Photo("P8") ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
let count = 1, un objet state = { count: 1 } et advance(), qui augmente les deux compteurs. Deux autres modules importent ces mêmes exportations. Le premier appelle advance(), puis effectue l’affectation state.count = 9. Que lit le second avec [count, state.count] et peut-il réaffecter count ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
save() et un bouton utilise onclick="save()" en ligne. Le module se charge correctement, mais un clic signale que save n’est pas défini. Comment faut-il relier l’événement ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
"inspect": "eslint src". Quelle commande exécute ce script avec l’argument --fix d’ESLint ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
no-undef. Quelle configuration supprime le conflit de mise en forme tout en conservant cette vérification utile des bogues ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
--write de Prettier modifie-t-elle directement ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
Un contrôle doit accepter les longueurs numériques finies de 0 à 12 cm inclus. Quels trois résultats attendus dans les tests révèlent des défauts distincts de cette implémentation ?
function setLength(n) {
if (n <= 0 || n > 12) throw new RangeError();
return n;
}
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
dist dans cette méthode de travail ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 5 : Combine les notions dans des projets inconnus10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Que renvoie new LoudNote("A").describe() ?
class Note {
constructor(text) { this.text = text; }
label() { return this.text; }
describe() { return "[" + this.label() + "]"; }
}
class LoudNote extends Note {
label() { return super.label() + "!"; }
describe() { return super.describe() + "?"; }
}
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
Que renvoie l’appel final ?
class Base {
constructor(n) { this.n = n; }
value() { return this.n; }
report() { return this.value() + 1; }
}
class Double extends Base {
value() { return super.value() * 2; }
}
class Extra extends Double {
report() { return super.report() + 3; }
}
new Extra(4).report();
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
feature.js possède une exportation par défaut et une exportation nommée ready. Un fichier barrel contient uniquement export * from "./feature.js";. Que transmet ce fichier barrel ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
document dans Node.js. Quelle réorganisation résout ces deux échecs ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
"private": true dans package.json ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
^0.6.2, verrouille la version 0.6.4 et possède un fichier de verrouillage valide et concordant. Le registre propose 0.6.9 et 0.7.0. Quelle suite indique la version après npm ci, puis la version autorisée la plus récente après un npm update délibéré ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
VITE_. Pourquoi cet emplacement ne convient-il pas à cet identifiant, même après minification ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
RangeError. Quelle assertion Vitest permet à la méthode de vérification d’observer l’exception ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
private value de TypeScript, lui affecte une entrée réseau analysée et renvoie un résultat numérique avec une formule incorrecte. Le projet passe tsc --noEmit. Quelle combinaison traite la confidentialité à l’exécution, l’entrée externe et le comportement ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Tu bloques sur ton propre exercice ? Aide aux Devoirs
Mots-Clés
A
Accesseur en écriture
Accesseur en lecture
thermostat.target peut invoquer un accesseur en lecture qui renvoie la température de consigne stockée dans un champ privé.Analyse statique
Annotation de type
Assertion
Audit des dépendances
Await au niveau supérieur
B
Bibliothèque
Bloc d’initialisation statique
Bundler
C
Carte d’importation
Carte des sources
Chaîne de prototypes
null lorsqu’il n’existe plus de prototype.Shape.prototype après que la recherche est passée par Circle.prototype.Champ d’instance
Champ privé
# et auquel seule la classe qui déclare ce nom peut accéder directement.Classe
Rectangle peut créer plusieurs formes dont les méthodes d’aire utilisent leurs propres largeurs et hauteurs.CommonJS
require synchrones et module.exports pour fournir des valeurs..cjs reste en CommonJS même lorsque le paquet qui le contient sélectionne explicitement les modules ES.Composant
Composition d’objets
Configuration à plat
Constructeur
D
Définition de type
Dépendance
Dépendance circulaire
Dépendance de développement
-D afin que sa version soit enregistrée pour le projet.Dépendance transitive
Développement piloté par les tests
E
Effacement des types
Effet de bord
Empreinte du contenu
Encapsulation
Espace de noms de module
default lorsqu’une exportation par défaut existe.Exécuteur de tests
Exportation nommée
MAX, qu’un fichier importateur peut demander directement ou renommer localement.Exportation par défaut
Circle même si un autre fichier lui donne un nom local différent.É
État
F
Fichier barrel
Fichier de verrouillage
npm ci pour recréer l’arbre verrouillé sur un autre ordinateur.Fonction constructeur
new pour initialiser des objets, souvent associée à des méthodes affectées à son prototype.area à Rectangle.prototype après avoir défini une fonction constructeur.Formateur de code
Fractionnement du code
Framework
H
Héritage
I
Importation dynamique
import() qui charge un module à la demande et renvoie une promesse dont le résultat sera son espace de noms.Installation propre
Instance
Interface
Interface publique
L
Liaison dynamique
Linter
M
Masquage
Membre statique
Méta-framework
Méthode
tile.area() permet à la méthode de lire les dimensions de tile par l’intermédiaire de l’objet receveur this.Méthode de fabrique statique
Rectangle.square(5) pour créer une instance dont les côtés sont de même longueur.Méthode privée
# et qui garde les opérations auxiliaires hors de l’interface publique de l’objet.this.#check(value) pour valider une entrée sans exposer cette fonction auxiliaire au code extérieur.Minification
Mixin
Module ES
import et export, avec sa propre portée et des règles d’exécution strictes.N
Niveau de sévérité d’une règle
npm
npx
O
Objet receveur
this lors d’un appel de méthode ordinaire.box.open(), l’objet receveur est box ; détacher open ne lie pas définitivement cet objet receveur.P
Paquet
Plage de versions
Polymorphisme
describe() sur des cercles et des rectangles, et chaque objet fournit l’aire appropriée.Préparer, agir, vérifier
Propriété énumérable
Object.keys énumère les clés de type chaîne propres et énumérables, tandis que for...in peut aussi inclure celles qui sont héritées.Propriété héritée
Object.hasOwn indique qu’il n’a pas de clé de thème propre.Propriété propre
Prototype
null, lié à un autre objet et consulté lorsque celui-ci ne possède pas lui-même une propriété demandée.R
Redéfinition de méthode
area() tout en continuant à hériter de la méthode de description partagée de la classe parente.Réexportation
Remplacement de modules à chaud
S
Script différé
Script npm
npm run lint.Serveur de développement
Sous-classe
extends qui hérite d’une superclasse et peut ajouter des membres ou spécialiser des comportements hérités.Spécificateur de module
"../utils.js" demande au système de chargement un module situé dans le dossier parent du fichier importateur.Spécificateur nu
Superclasse
T
Test aux limites
Test unitaire
Tolérance
Tree shaking
TypeScript
V
Validation à l’exécution
Versionnement sémantique
À propos de cette unité
Crée ton propre Matériel d'Étude
Transforme tes notes, diapos et documents en Guides d'Étude, Questions Pratiques, Flashcards et 6 autres outils.