Dans la programmation orientée objet, une classe est un modèle de code de programme extensible pour créer des objets, fournissant des valeurs initiales pour l'état (variables membres) et des implémentations de comportement (fonctions ou méthodes membres).
En pratique, nous devons souvent créer de nombreux objets du même type, comme des utilisateurs, des biens ou autre.
Comme nous le savons déjà dans le chapitre Constructeur, l'opérateur "new", new function
peut vous aider.
Mais dans le JavaScript moderne, il existe une construction de « classe » plus avancée, qui introduit de nouvelles fonctionnalités intéressantes utiles pour la programmation orientée objet.
La syntaxe de base est la suivante :
classe MaClasse { // méthodes de classe constructeur() { ... } méthode1() { ... } méthode2() { ... } méthode3() { ... } ... }
Utilisez ensuite new MyClass()
pour créer un nouvel objet avec toutes les méthodes répertoriées.
La méthode constructor()
est appelée automatiquement par new
, nous pouvons donc y initialiser l'objet.
Par exemple:
Utilisateur de classe { constructeur (nom) { this.name = nom ; } disBonjour() { alert(ce.nom); } } // Utilisation : let user = new User("John"); user.sayHi();
Lorsque new User("John")
est appelé :
Un nouvel objet est créé.
Le constructor
s'exécute avec l'argument donné et l'assigne à this.name
.
…Ensuite, nous pouvons appeler des méthodes objet, telles que user.sayHi()
.
Pas de virgule entre les méthodes de classe
Un piège courant pour les développeurs débutants est de mettre une virgule entre les méthodes de classe, ce qui entraînerait une erreur de syntaxe.
La notation ici ne doit pas être confondue avec les littéraux d'objet. Au sein de la classe, aucune virgule n’est requise.
Alors, qu’est-ce qu’un class
exactement ? Il ne s’agit pas d’une entité entièrement nouvelle au niveau linguistique, comme on pourrait le penser.
Dévoilons toute magie et voyons ce qu'est réellement une classe. Cela aidera à comprendre de nombreux aspects complexes.
En JavaScript, une classe est une sorte de fonction.
Ici, jetez un œil :
Utilisateur de classe { constructeur (nom) { this.name = nom ; } sayHi() { alert(this.name); } } // preuve : l'utilisateur est une fonction alert (type d'utilisateur) ; // fonction
Ce que fait réellement la construction class User {...}
est :
Crée une fonction nommée User
, qui devient le résultat de la déclaration de classe. Le code de la fonction est extrait de la méthode constructor
(supposé vide si nous n'écrivons pas une telle méthode).
Stocke les méthodes de classe, telles que sayHi
, dans User.prototype
.
Une fois new User
créé, lorsque nous appelons sa méthode, elle est extraite du prototype, comme décrit dans le chapitre F.prototype. L'objet a donc accès aux méthodes de classe.
Nous pouvons illustrer le résultat de la déclaration class User
comme :
Voici le code pour l'introspecter :
Utilisateur de classe { constructeur (nom) { this.name = nom ; } sayHi() { alert(this.name); } } // la classe est une fonction alert (type d'utilisateur) ; // fonction // ...ou, plus précisément, la méthode constructeur alert(Utilisateur === Utilisateur.prototype.constructor); // vrai // Les méthodes sont dans User.prototype, par exemple : alerte (Utilisateur.prototype.sayHi); // le code de la méthode sayHi // il y a exactement deux méthodes dans le prototype alert(Object.getOwnPropertyNames(User.prototype)); // constructeur, disBonjour
Parfois, les gens disent que class
est un « sucre syntaxique » (une syntaxe conçue pour rendre les choses plus faciles à lire, mais qui n'introduit rien de nouveau), car nous pourrions en fait déclarer la même chose sans utiliser du tout le mot-clé class
:
// réécriture de la classe User en fonctions pures // 1. Créer une fonction constructeur fonction Utilisateur (nom) { this.name = nom ; } // un prototype de fonction a la propriété "constructeur" par défaut, // donc nous n'avons pas besoin de le créer // 2. Ajouter la méthode au prototype User.prototype.sayHi = fonction() { alert(ce.nom); } ; // Utilisation : let user = new User("Jean"); user.sayHi();
Le résultat de cette définition est à peu près le même. Il y a donc effectivement des raisons pour lesquelles class
peut être considérée comme un sucre syntaxique pour définir un constructeur avec ses méthodes prototypes.
Il existe néanmoins des différences importantes.
Premièrement, une fonction créée par class
est étiquetée par une propriété interne spéciale [[IsClassConstructor]]: true
. Ce n’est donc pas tout à fait la même chose que de le créer manuellement.
La langue vérifie cette propriété à divers endroits. Par exemple, contrairement à une fonction normale, elle doit être appelée avec new
:
Utilisateur de classe { constructeur() {} } alert (type d'utilisateur) ; // fonction Utilisateur(); // Erreur : l'utilisateur du constructeur de classe ne peut pas être invoqué sans "nouveau"
De plus, une représentation sous forme de chaîne d'un constructeur de classe dans la plupart des moteurs JavaScript commence par la « classe… »
Utilisateur de classe { constructeur() {} } alerte (utilisateur); // classe Utilisateur { ... }
Il existe d'autres différences, nous les verrons bientôt.
Les méthodes de classe ne sont pas dénombrables. Une définition de classe définit l'indicateur enumerable
sur false
pour toutes les méthodes du "prototype"
.
C'est bien, car si nous for..in
sur un objet, nous ne voulons généralement pas de ses méthodes de classe.
Les classes use strict
. Tout le code à l’intérieur de la construction de classe est automatiquement en mode strict.
En outre, la syntaxe class
apporte de nombreuses autres fonctionnalités que nous explorerons plus tard.
Tout comme les fonctions, les classes peuvent être définies dans une autre expression, transmises, renvoyées, attribuées, etc.
Voici un exemple d'expression de classe :
laissez l'utilisateur = classe { disBonjour() { alert("Bonjour"); } } ;
Semblables aux expressions de fonction nommées, les expressions de classe peuvent avoir un nom.
Si une expression de classe a un nom, celui-ci est visible uniquement à l'intérieur de la classe :
// "Expression de classe nommée" // (aucun terme de ce type dans la spécification, mais c'est similaire à l'expression de fonction nommée) let User = classe MaClasse { disBonjour() { alerte (MaClasse); // Le nom MyClass est visible uniquement à l'intérieur de la classe } } ; nouvel utilisateur().sayHi(); // fonctionne, affiche la définition de MyClass alerte (MaClasse); // erreur, le nom MyClass n'est pas visible en dehors de la classe
On peut même faire des cours dynamiquement « à la demande », comme ceci :
fonction makeClass (phrase) { // déclare une classe et la renvoie classe de retour { disBonjour() { alerte(phrase); } } ; } // Crée une nouvelle classe let User = makeClass("Bonjour"); nouvel utilisateur().sayHi(); // Bonjour
Tout comme les objets littéraux, les classes peuvent inclure des getters/setters, des propriétés calculées, etc.
Voici un exemple pour user.name
implémenté à l'aide de get/set
:
Utilisateur de classe { constructeur (nom) { // invoque le setter this.name = nom ; } obtenir le nom() { renvoie this._name ; } définir le nom (valeur) { si (valeur.longueur < 4) { alert("Le nom est trop court."); retour; } this._name = valeur ; } } let user = new User("Jean"); alert(utilisateur.nom); // John utilisateur = nouvel utilisateur(""); // Le nom est trop court.
Techniquement, une telle déclaration de classe fonctionne en créant des getters et des setters dans User.prototype
.
Voici un exemple avec un nom de méthode calculé en utilisant des parenthèses [...]
:
Utilisateur de classe { ['dire' + 'Salut']() { alert("Bonjour"); } } nouvel utilisateur().sayHi();
De telles caractéristiques sont faciles à retenir, car elles ressemblent à celles des objets littéraux.
Les anciens navigateurs peuvent avoir besoin d'un polyfill
Les champs de classe sont un ajout récent au langage.
Auparavant, nos classes n'avaient que des méthodes.
« Champs de classe » est une syntaxe qui permet d'ajouter des propriétés.
Par exemple, ajoutons la propriété name
à class User
:
Utilisateur de classe { nom = "Jean" ; disBonjour() { alert(`Bonjour, ${this.name}!`); } } nouvel utilisateur().sayHi(); // Bonjour, Jean !
Alors, nous écrivons simplement «
La différence importante entre les champs de classe est qu'ils sont définis sur des objets individuels, et non sur User.prototype
:
Utilisateur de classe { nom = "Jean" ; } let user = nouvel utilisateur (); alert(utilisateur.nom); // John alerte (Utilisateur.prototype.nom); // non défini
Nous pouvons également attribuer des valeurs à l'aide d'expressions et d'appels de fonctions plus complexes :
Utilisateur de classe { name = prompt("Nom, s'il vous plaît?", "John"); } let user = nouvel utilisateur (); alert(utilisateur.nom); // John
Comme démontré dans le chapitre Les fonctions de liaison de fonctions en JavaScript ont une dynamique this
. Cela dépend du contexte de l'appel.
Ainsi, si une méthode objet est transmise et appelée dans un autre contexte, this
ne sera plus une référence à son objet.
Par exemple, ce code affichera undefined
:
Bouton de classe { constructeur (valeur) { this.value = valeur ; } clic() { alert(this.value); } } let bouton = nouveau bouton("bonjour"); setTimeout (bouton. clic, 1000); // non défini
Le problème s'appelle « perdre this
».
Il existe deux approches pour résoudre ce problème, comme indiqué dans le chapitre Liaison de fonctions :
Passez une fonction wrapper, telle que setTimeout(() => button.click(), 1000)
.
Liez la méthode à un objet, par exemple dans le constructeur.
Les champs de classe fournissent une autre syntaxe assez élégante :
Bouton de classe { constructeur (valeur) { this.value = valeur ; } cliquez = () => { alert(this.value); } } let bouton = nouveau bouton("bonjour"); setTimeout (bouton. clic, 1000); // Bonjour
Le champ de classe click = () => {...}
est créé pour chaque objet, il existe une fonction distincte pour chaque objet Button
, avec this
à l'intérieur faisant référence à cet objet. Nous pouvons transmettre button.click
n'importe où, et la valeur de this
sera toujours correcte.
C'est particulièrement utile dans un environnement de navigateur, pour les écouteurs d'événements.
La syntaxe de base de la classe ressemble à ceci :
classe MaClasse { accessoire = valeur ; // propriété constructeur(...) { // constructeur //... } méthode(...) {} // méthode obtenir quelque chose(...) {} // méthode getter définir quelque chose(...) {} // méthode setter [Symbol.iterator]() {} // méthode avec nom calculé (symbole ici) //... }
MyClass
est techniquement une fonction (celle que nous fournissons en tant que constructor
), tandis que les méthodes, getters et setters sont écrits dans MyClass.prototype
.
Dans les prochains chapitres, nous en apprendrons davantage sur les classes, y compris l'héritage et d'autres fonctionnalités.
importance : 5
La classe Clock
(voir le bac à sable) est écrite dans un style fonctionnel. Réécrivez-le dans la syntaxe « classe ».
PS L'horloge tourne dans la console, ouvrez-la pour voir.
Ouvrez un bac à sable pour la tâche.
horloge de classe { constructeur ({ modèle }) { this.template = modèle ; } rendre() { let date = new Date(); let hours = date.getHours(); si (heures < 10) heures = '0' + heures ; let mins = date.getMinutes(); si (mins < 10) mins = '0' + min ; let secs = date.getSeconds(); si (secs < 10) secs = '0' + secs ; laisser la sortie = this.template .replace('h', heures) .replace('m', minutes) .replace('s', secondes); console.log(sortie); } arrêt() { clearInterval(this.timer); } commencer() { this.render(); this.timer = setInterval(() => this.render(), 1000); } } let clock = new Clock({template: 'h:m:s'}); horloge.start();
Ouvrez la solution dans un bac à sable.