Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
Angular

Les classes et les modules ES6 (import / export)

Cette leçon présente les deux piliers de l'architecture d'Angular : les classes et les modules. Vous apprendrez à déclarer une classe avec son constructeur, ses propriétés et ses méthodes, à utiliser ...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À propos de ce cours

Cette leçon présente les deux piliers de l'architecture d'Angular : les classes et les modules. Vous apprendrez à déclarer une classe avec son constructeur, ses propriétés et ses méthodes, à utiliser les accesseurs get et set, les membres static et les champs privés #, puis à mettre en œuvre l'héritage avec extends et super. Vous découperez ensuite un programme en modules avec export et import, en distinguant export nommé et export par défaut, organiserez un dossier avec un fichier baril et chargerez un module à la demande avec import(). Des schémas, un composant Angular annoté et cinq activités pratiques accompagnent chaque notion.
  1. Objectifs

    • Déclarer une classe avec son constructeur, ses propriétés et ses méthodes, puis créer des instances avec new
    • Utiliser les accesseurs get / set, les membres static et les membres privés #
    • Mettre en œuvre l'héritage avec extends et super
    • Découper un programme en modules avec export et import
    • Distinguer export nommé et export par défaut, et créer un fichier « baril » (index)
    • Charger un module à la demande avec import()
    • Reconnaître ces notions dans l'architecture d'un projet Angular
  2. Présentation

    • Les classes et les modules sont deux nouveautés majeures d'ES6 (ES2015), complétées par les versions suivantes (champs publics, membres privés # et static en ES2022).
    • Une classe est un modèle qui décrit les données (propriétés) et les actions (méthodes) d'un type d'objet. Chaque objet créé à partir de ce modèle est une instance.
    • Un module est un fichier qui exporte ce qu'il veut partager ; les autres fichiers l'importent. Un projet devient ainsi un ensemble de petits fichiers clairs et indépendants.
    • Ces deux notions sont la base de toute l'architecture d'Angular: chaque composant, service, pipe ou directive est une classe, rangée dans son propre fichier, exportée puis importée là où on l'utilise.

Une classe et ses instances

  1. Déclarer une classe

    • Une classe se déclare avec le mot-clé class suivi de son nom, écrit en PascalCase (majuscule à chaque mot : Stagiaire, ListeFormations).
    • Elle contient :
      • des champs (propriétés déclarées directement dans la classe, avec une valeur par défaut) ;
      • un constructeur constructor(), exécuté automatiquement par new pour initialiser l'objet ;
      • des méthodes: les actions de l'objet, qui utilisent this pour accéder à ses propriétés.
class Stagiaire {
  groupe = 'Non affecté';     // champ public avec une valeur par défaut
  notes = [];

  constructor(nom, prenom) {  // appelé par new
    this.nom = nom;
    this.prenom = prenom;
  }

  ajouterNote(note) {         // méthode
    this.notes.push(note);
    return this;              // renvoyer this permet d'enchaîner les appels
  }

  presenter() {
    return `${this.prenom} ${this.nom} (${this.groupe})`;
  }
}

const amira = new Stagiaire('Ben Ali', 'Amira');   // une instance
amira.groupe = 'DEV1';
amira.ajouterNote(14).ajouterNote(16).ajouterNote(17);

const sami = new Stagiaire('Trabelsi', 'Sami');     // une autre instance
console.log(amira.presenter());     // Amira Ben Ali (DEV1)
console.log(sami.presenter());      // Sami Trabelsi (Non affecté)
console.log(amira.notes === sami.notes);   // false : chaque instance a ses propres données
console.log(amira instanceof Stagiaire);   // true
  • À savoir : une classe doit toujours être appelée avec new (TypeError: Class constructor Stagiaire cannot be invoked without 'new'), et elle n'est pas remontée comme une fonction : on ne peut pas l'utiliser avant sa déclaration (ReferenceError: Cannot access 'Stagiaire' before initialization).

Anatomie d'une classe JavaScript

  1. Les accesseurs get et set

    • Un accesseur est une méthode qui s'utilise comme une propriété, sans parenthèses :
      • get calcule une valeur à chaque lecture (une moyenne, un nom complet, un prix TTC…) ;
      • set est appelé lors d'une affectation, ce qui permet de contrôler ou transformer la valeur reçue.
class Stagiaire {
  notes = [];

  constructor(nom, prenom) {
    this.nom = nom;
    this.prenom = prenom;
  }

  get moyenne() {
    if (this.notes.length === 0) return 0;
    return this.notes.reduce((s, n) => s + n, 0) / this.notes.length;
  }

  get nomComplet() {
    return `${this.prenom} ${this.nom.toUpperCase()}`;
  }

  set nomComplet(valeur) {
    const [prenom, ...reste] = valeur.trim().split(' ');
    this.prenom = prenom;
    this.nom = reste.join(' ');
  }
}

const s = new Stagiaire('Trabelsi', 'Sami');
s.notes.push(12, 15);
console.log(s.moyenne);          // 13.5 : calculé, sans parenthèses
console.log(s.nomComplet);       // Sami TRABELSI

s.nomComplet = 'Sami Ben Salah'; // appelle le set
console.log(s.nom);              // Ben Salah

s.moyenne = 20;                  // pas de set : affectation ignorée
// (en mode strict, ou dans un module : TypeError: Cannot set property moyenne
//  of #<Stagiaire> which has only a getter)
  1. Les membres statiques et les membres privés

    • Un membre static appartient à la classe elle-même, pas à ses instances. On l'utilise avec le nom de la classe : Formation.TVA, Formation.gratuite(). C'est utile pour les constantes communes, les compteurs et les « fabriques » d'objets.
    • Un membre dont le nom commence par # est privé: il n'est accessible que depuis le code de la classe. C'est l'encapsulation: l'objet protège ses données et impose de passer par ses méthodes.
class Formation {
  static TVA = 0.19;              // constante partagée
  static #nombre = 0;             // compteur privé et statique
  #inscrits = [];                 // données privées de chaque formation
  #capacite;

  constructor(titre, capacite = 20, prixHT = 0) {
    this.titre = titre;
    this.prixHT = prixHT;
    this.#capacite = capacite;
    Formation.#nombre++;
  }

  static get nombre() {           // accesseur statique
    return Formation.#nombre;
  }

  static gratuite(titre) {        // méthode statique « fabrique »
    return new Formation(titre, 30, 0);
  }

  get placesRestantes() {
    return this.#capacite - this.#inscrits.length;
  }

  get prixTTC() {
    return +(this.prixHT * (1 + Formation.TVA)).toFixed(3);
  }

  set capacite(valeur) {          // contrôle de la valeur
    if (!Number.isInteger(valeur) || valeur < this.#inscrits.length) {
      throw new RangeError(`Capacité invalide : ${valeur}`);
    }
    this.#capacite = valeur;
  }

  inscrire(nom) {
    if (this.#estComplete()) return `${nom} : refusé, ${this.titre} est complète`;
    this.#inscrits.push(nom);
    return `${nom} inscrit(e) en ${this.titre}`;
  }

  #estComplete() {                // méthode privée
    return this.placesRestantes === 0;
  }
}

const web = new Formation('Développement web', 2, 1200);
console.log(web.inscrire('Amira'));   // Amira inscrit(e) en Développement web
console.log(web.inscrire('Sami'));    // Sami inscrit(e) en Développement web
console.log(web.inscrire('Karim'));   // Karim : refusé, Développement web est complète
console.log(web.placesRestantes, web.prixTTC);   // 0 1428

const atelier = Formation.gratuite('Atelier Git');
console.log(Formation.nombre);        // 2

web.capacite = 1;                     // RangeError: Capacité invalide : 1
console.log(web.inscrits);            // undefined : le champ privé n'est pas visible
// console.log(web.#inscrits);        // SyntaxError: Private field '#inscrits' must be
                                      // declared in an enclosing class
console.log(Object.keys(web));        // [ 'titre', 'prixHT' ]
  1. L'héritage : extends et super

    • Une classe peut hériter d'une autre avec extends: elle récupère toutes ses propriétés et méthodes, puis ajoute ou redéfinit ce qui lui est propre.
    • super(…) appelle le constructeur de la classe parente. Dans une classe dérivée, il est obligatoire et doit être appelé avant toute utilisation de this.
    • super.methode() appelle la version parente d'une méthode redéfinie.

L'héritage avec extends et super

class Personne {
  constructor(nom, prenom) {
    this.nom = nom;
    this.prenom = prenom;
  }
  get nomComplet() {
    return `${this.prenom} ${this.nom}`;
  }
  presenter() {
    return `Je m'appelle ${this.nomComplet}`;
  }
  toString() {                    // utilisée quand l'objet est converti en texte
    return this.nomComplet;
  }
}

class Stagiaire extends Personne {
  constructor(nom, prenom, groupe) {
    super(nom, prenom);
    this.groupe = groupe;
  }
  presenter() {
    return `${super.presenter()}, stagiaire en ${this.groupe}`;
  }
}

class Formateur extends Personne {
  constructor(nom, prenom, specialite) {
    super(nom, prenom);
    this.specialite = specialite;
  }
  presenter() {
    return `${super.presenter()}, formateur en ${this.specialite}`;
  }
}

const personnes = [
  new Stagiaire('Ben Ali', 'Amira', 'DEV1'),
  new Formateur('Gharbi', 'Ali', 'Angular'),
  new Personne('Mansour', 'Leila'),
];
for (const p of personnes) {
  console.log(p.presenter());     // chaque objet utilise SA version de presenter()
}
// Je m'appelle Amira Ben Ali, stagiaire en DEV1
// Je m'appelle Ali Gharbi, formateur en Angular
// Je m'appelle Leila Mansour

const ali = personnes[1];
console.log(ali instanceof Formateur, ali instanceof Personne, ali instanceof Stagiaire);
// true true false
console.log(`Responsable : ${ali}`);   // Responsable : Ali Gharbi (toString)
  • Appeler la même méthode sur des objets de classes différentes et obtenir un comportement adapté à chacun s'appelle le polymorphisme.
  • Oubli de super : ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor.
  • Sous le capot : les classes sont une écriture plus claire (« sucre syntaxique ») du système de prototypes de JavaScript : typeof Stagiaire vaut 'function'. Il n'est pas nécessaire de connaître les prototypes pour utiliser les classes.
  • Les modules : export et import

    • Un module est un fichier JavaScript qui a sa propre portée: ses variables, fonctions et classes sont invisibles des autres fichiers, sauf celles qu'il exporte.
    • Les modules s'exécutent toujours en mode strict, et chaque module n'est exécuté qu'une seule fois, même s'il est importé par plusieurs fichiers.
    • Pour les utiliser :
      • avec Node.js : extension .mjs, ou "type": "module" dans package.json;
      • dans un navigateur : <script type="module" src="app.js"></script>, la page devant être servie par un serveur web ;
      • dans Angular : tous les fichiers .ts sont déjà des modules, rien à configurer.
  • L'export nommé

    • On place export devant chaque élément à partager. Un fichier peut avoir autant d'exports nommés que nécessaire. Ils s'importent entre accolades, avec le même nom.
    // utils/calculs.mjs
    export const SEUIL_ADMISSION = 10;
    
    export function moyenne(notes) {
      if (notes.length === 0) return 0;
      return notes.reduce((s, n) => s + n, 0) / notes.length;
    }
    
    export const estAdmis = (stagiaire) => stagiaire.moyenne >= SEUIL_ADMISSION;
    // app.mjs
    import { moyenne, estAdmis } from './utils/calculs.mjs';          // le chemin commence par ./ ou ../
    import { SEUIL_ADMISSION as seuil } from './utils/calculs.mjs';   // renommer avec as
    import * as calculs from './utils/calculs.mjs';                   // tout le module dans un objet
    
    console.log(moyenne([12, 15]), seuil, calculs.SEUIL_ADMISSION);   // 13.5 10 10
    • On peut aussi regrouper les exports en fin de fichier : export { moyenne, estAdmis, SEUIL_ADMISSION };.
  • L'export par défaut

    • Un fichier peut avoir un seul export par défaut, qui s'importe sans accolades, avec le nom de son choix. On peut combiner export par défaut et exports nommés.
    // utils/calculs.mjs (suite)
    export default function arrondir(valeur, decimales = 2) {
      return Number(valeur.toFixed(decimales));
    }
    // app.mjs
    import arrondir, { moyenne } from './utils/calculs.mjs';   // défaut + nommé
    console.log(arrondir(moyenne([12.456, 15.1])));            // 13.78
      Export nommé Export par défaut
    Nombre par fichier illimité un seul
    Syntaxe d'export export class Stagiaire { } export default class Stagiaire { }
    Syntaxe d'import import { Stagiaire } from … import Stagiaire from …
    Nom à l'import le même (ou renommé avec as) libre
    Dans Angular presque toujours rare
    • Conseil : préférez les exports nommés. Le nom est imposé, ce qui évite les incohérences, et VS Code peut ajouter automatiquement l'import quand vous tapez le nom d'une classe (Ctrl + .).
  • Organiser un projet : le fichier baril

    • Quand un dossier contient plusieurs modules, on peut créer un fichier index qui réexporte leur contenu. Ce fichier « baril » (barrel) offre un seul point d'entrée au dossier.

    Organiser un projet JavaScript en modules

    centre/
    ├── models/
    │   ├── personne.mjs
    │   ├── stagiaire.mjs
    │   ├── formateur.mjs
    │   └── index.mjs      ← baril
    ├── utils/
    │   ├── calculs.mjs
    │   └── rapport.mjs
    └── app.mjs
    // models/stagiaire.mjs
    import { Personne } from './personne.mjs';
    import { moyenne } from '../utils/calculs.mjs';
    
    export class Stagiaire extends Personne {
      constructor(nom, prenom, groupe, notes = []) {
        super(nom, prenom);
        this.groupe = groupe;
        this.notes = notes;
      }
      get moyenne() {
        return moyenne(this.notes);
      }
      presenter() {
        return `${super.presenter()}, stagiaire en ${this.groupe} (${this.moyenne.toFixed(2)})`;
      }
    }
    // models/index.mjs : le baril
    export { Personne } from './personne.mjs';
    export { Stagiaire } from './stagiaire.mjs';
    export { Formateur } from './formateur.mjs';
    // app.mjs : un seul import pour tout le dossier models
    import { Stagiaire, Formateur } from './models/index.mjs';
    import { estAdmis, SEUIL_ADMISSION as seuil } from './utils/calculs.mjs';
    
    const groupe = [
      new Stagiaire('Ben Ali', 'Amira', 'DEV1', [14, 16, 17]),
      new Stagiaire('Trabelsi', 'Sami', 'DEV1', [8, 11, 9]),
    ];
    const formateur = new Formateur('Gharbi', 'Ali', 'Angular');
    
    console.log(formateur.presenter());
    groupe.forEach((s) => console.log(s.presenter()));
    console.log(`Admis (seuil ${seuil}) :`, groupe.filter(estAdmis).map((s) => s.prenom));
    node app.mjs
    Je m'appelle Ali Gharbi, formateur en Angular
    Je m'appelle Amira Ben Ali, stagiaire en DEV1 (15.67)
    Je m'appelle Sami Trabelsi, stagiaire en DEV1 (9.33)
    Admis (seuil 10) : [ 'Amira' ]
    1. Charger un module à la demande : import()

      • La fonction import() charge un module pendant l'exécution, seulement quand on en a besoin. Elle renvoie une promesse: on l'utilise avec await ou .then().
      • Le code du module n'est pas chargé au démarrage : l'application démarre plus vite.
    // utils/rapport.mjs
    console.log('(module rapport chargé)');
    
    export function genererRapport(stagiaires) {
      const lignes = stagiaires.map((s) => `- ${s.nomComplet} : ${s.moyenne.toFixed(2)}`);
      return ['Rapport du groupe', ...lignes].join('\n');
    }
    // app.mjs (suite)
    if (process.argv.includes('--rapport')) {
      const { genererRapport } = await import('./utils/rapport.mjs');
      console.log(genererRapport(groupe));
    }
    node app.mjs --rapport
    …
    (module rapport chargé)
    Rapport du groupe
    - Amira Ben Ali : 15.67
    - Sami Trabelsi : 9.33
    • Sans l'option --rapport, le message « module rapport chargé » n'apparaît pas : le fichier n'a jamais été lu.
  • Classes et modules dans Angular

    • Un fichier Angular est un module qui importe ce dont il a besoin et exporte sa classe :
    // src/app/stagiaires/liste-stagiaires.ts
    import { Component, OnInit } from '@angular/core';             // paquet npm : pas de ./
    import { StagiaireService } from '../services/stagiaire.service'; // fichier du projet : chemin relatif
    
    @Component({
      selector: 'app-liste-stagiaires',
      templateUrl: './liste-stagiaires.html',
    })
    export class ListeStagiaires implements OnInit {                // export nommé d'une classe
      titre = 'Mes stagiaires';
      protected stagiaires: string[] = [];
      readonly capacite = 25;
    
      constructor(private service: StagiaireService) {}             // propriété déclarée dans le constructeur
    
      ngOnInit(): void {
        this.stagiaires = this.service.lister();
      }
    
      get nombre(): number {                                        // accesseur
        return this.stagiaires.length;
      }
    }
    • TypeScript complète les classes ECMAScript avec quelques ajouts que vous verrez dans le chapitre Programmer en TypeScript:
      • les types des propriétés et des paramètres (stagiaires: string[]) ;
      • les modificateurs d'accès public, protected et private, vérifiés à la compilation (par exemple TS2341: Property 'service' is private and only accessible within class 'ListeStagiaires'.) ;
      • les propriétés de paramètre: constructor(private service: StagiaireService) {} déclare et remplit la propriété en une ligne ;
      • implements pour respecter une interface (OnInit impose la méthode ngOnInit()) ;
      • les décorateurs comme @Component, qui ajoutent des informations à la classe.
    • Chargement à la demande: Angular utilise import() pour ne charger une page que lorsque l'utilisateur la visite (lazy loading), vu dans le chapitre Routage:
    // app.routes.ts
    export const routes: Routes = [
      {
        path: 'admin',
        loadComponent: () => import('./admin/admin').then((m) => m.Admin),
      },
    ];
    1. Activité 1 : la classe Stagiaire

      • Dans le dossier rappel-ecmascript (dépôt GitHub), créer le fichier 24-stagiaire.js et écrire une classe Stagiaire avec :
        • les champs groupe = 'Non affecté' et notes = [], et un constructeur (nom, prenom);
        • une méthode ajouterNote(note) qui renvoie this pour permettre l'enchaînement ;
        • les accesseurs moyenne (0 sans note) et nomComplet (nom en majuscules), et un set nomComplet qui découpe « Prénom Nom » ;
        • une méthode presenter().
      • Tester avec le code suivant :
    const amira = new Stagiaire('Ben Ali', 'Amira');
    amira.groupe = 'DEV1';
    amira.ajouterNote(14).ajouterNote(16).ajouterNote(17);
    console.log(amira.presenter());
    
    const sami = new Stagiaire('Trabelsi', 'Sami');
    console.log(sami.presenter());
    sami.nomComplet = 'Sami Ben Salah';
    console.log(sami.nom, '|', sami.prenom);
    
    // Résultat attendu :
    // Amira BEN ALI (DEV1) – moyenne 15.67
    // Sami TRABELSI (Non affecté) – moyenne 0.00
    // Ben Salah | Sami
    • Répondre en commentaire : que se passe-t-il si on déclare notes = [] dans le constructeur plutôt qu'en champ ? Et si on oublie new?
  • Activité 2 : encapsuler une formation

    • Dans 25-formation.js, écrire la classe Formation avec un champ statique TVA, un compteur statique privé #nombre lisible par un accesseur statique, des champs privés #inscrits et #capacite, une méthode privée #estComplete(), les accesseurs placesRestantes et prixTTC, un set capacite qui lève une RangeError si la valeur est invalide, une méthode inscrire(nom) et une méthode statique gratuite(titre).
    • Tester :
    const web = new Formation('Développement web', 2, 1200);
    console.log(web.inscrire('Amira'), '|', web.inscrire('Sami'), '|', web.inscrire('Karim'));
    console.log(web.placesRestantes, web.prixTTC, web.inscrits, Formation.nombre);
    const atelier = Formation.gratuite('Atelier Git');
    console.log(atelier.titre, atelier.placesRestantes, Formation.nombre);
    try {
      web.capacite = 1;
    } catch (e) {
      console.log(e.name + ': ' + e.message);
    }
    web.capacite = 5;
    console.log(web.placesRestantes);
    
    // Résultat attendu :
    // Amira inscrit(e) en Développement web | Sami inscrit(e) en Développement web | Karim : refusé, Développement web est complète
    // 0 1428 undefined 1
    // Atelier Git 30 2
    // RangeError: Capacité invalide : 1
    // 3
    • Expliquer en commentaire pourquoi web.inscrits vaut undefined, et l'intérêt d'obliger à passer par inscrire().
  • Activité 3 : héritage et polymorphisme

    • Dans 26-heritage.js, écrire les classes Personne, Stagiaire et Formateur de la leçon, puis ajouter une troisième classe dérivée Administratif (propriété service, par exemple « Scolarité »).
    • Créer un tableau contenant une instance de chaque classe et afficher presenter() pour chacune avec une boucle for…of.
    • Avec filter et instanceof, n'afficher que les formateurs, puis compter le nombre de personnes qui sont des Personne.
    • Supprimer l'appel à super(nom, prenom) dans un constructeur : noter le message d'erreur, puis le remettre.
  • Activité 4 : découper en modules

    • Créer le dossier 27-centre/ avec l'arborescence de la leçon : models/ (une classe par fichier et un baril index.mjs), utils/calculs.mjs (exports nommés et un export par défaut) et app.mjs.
    • Exécuter node 27-centre/app.mjs: le résultat doit être celui de la leçon.
    • Provoquer les trois erreurs suivantes une par une, noter le message obtenu en commentaire, puis corriger :
    Modification Message attendu
    importer { SEUIL } au lieu de { SEUIL_ADMISSION } SyntaxError: The requested module './utils/calculs.mjs' does not provide an export named 'SEUIL'
    écrire './models/personne' sans l'extension .mjs Error [ERR_MODULE_NOT_FOUND]: Cannot find module …
    import Stagiaire from './models/stagiaire.mjs' (sans accolades) SyntaxError: … does not provide an export named 'default'
    • Remarque : Node.js exige l'extension complète du fichier dans un import relatif. Dans Angular, on l'omet ('./models/stagiaire') : c'est l'outil de compilation qui retrouve le fichier.
  • Activité 5 : chargement à la demande

    • Ajouter le module utils/rapport.mjs et le chargement avec await import() présentés dans la leçon.
    • Exécuter node 27-centre/app.mjs, puis node 27-centre/app.mjs --rapport. Expliquer en commentaire pourquoi le message « (module rapport chargé) » n'apparaît que dans le second cas.
    • Ajouter console.log('(module calculs chargé)'); en haut de utils/calculs.mjs. Ce module est importé par app.mjs et par stagiaire.mjs: combien de fois le message s'affiche-t-il ? Pourquoi ?
    • Faire un commit après chaque activité, puis un push : git commit -m "Classes et modules : activité N". Mettre à jour le README.md du dépôt avec la liste des activités de la section.
  • Erreurs fréquentes

  • Message Cause Solution
    TypeError: Class constructor X cannot be invoked without 'new' Classe appelée comme une fonction Écrire new X(…)
    ReferenceError: Cannot access 'X' before initialization Classe utilisée avant sa déclaration Déclarer la classe avant de l'utiliser
    ReferenceError: Must call super constructor in derived class … super() oublié, ou appelé après this Appeler super(…) en premier dans le constructeur
    SyntaxError: Private field '#x' must be declared in an enclosing class Accès à un champ privé hors de la classe, ou champ non déclaré Déclarer #x dans la classe et passer par une méthode ou un accesseur
    TypeError: Cannot set property x of #<Y> which has only a getter Affectation d'un accesseur sans set Ajouter un set, ou ne pas modifier cette valeur calculée
    SyntaxError: The requested module … does not provide an export named 'x' Nom mal orthographié, export oublié, ou export par défaut importé entre accolades (et inversement) Vérifier le nom et le type d'export
    SyntaxError: Cannot use import statement outside a module Script classique : navigateur sans type="module", ou ancienne version de Node.js avec un fichier .js type="module", extension .mjs ou "type": "module"
    Error [ERR_MODULE_NOT_FOUND] Chemin incorrect ou extension oubliée (Node.js) Vérifier le chemin relatif (./, ../) et l'extension
    1. À retenir

      • Une classe est un modèle ; new crée une instance et appelle le constructor.
      • get / set s'utilisent comme des propriétés ; static appartient à la classe ; # rend un membre privé.
      • extends permet l'héritage ; super() appelle le constructeur parent, obligatoirement avant this.
      • Un module a sa propre portée et ne partage que ce qu'il exporte.
      • Export nommé : import { X }; export par défaut : import X. Angular utilise les exports nommés.
      • Un fichier baril index regroupe les exports d'un dossier ; import() charge un module à la demande.
    Un composant Angular, c'est une classe exportée par un module. Avec cette leçon, vous avez toutes les bases d'ECMAScript pour lire et écrire le code d'Angular.