Nous pouvons également attribuer une méthode à la classe dans son ensemble. De telles méthodes sont appelées statiques .
Dans une déclaration de classe, ils sont précédés d'un mot-clé static
, comme ceci :
Utilisateur de classe { statique méthodestatique() { alert(this === Utilisateur); } } User.staticMethod(); // vrai
Cela revient en fait à l'attribuer directement en tant que propriété :
Utilisateur de classe { } Utilisateur.staticMethod = fonction() { alert(this === Utilisateur); } ; User.staticMethod(); // vrai
La valeur de this
dans l'appel User.staticMethod()
est le constructeur de classe User
lui-même (la règle « objet avant point »).
Habituellement, les méthodes statiques sont utilisées pour implémenter des fonctions qui appartiennent à la classe dans son ensemble, mais pas à un objet particulier de celle-ci.
Par exemple, nous avons des objets Article
et avons besoin d’une fonction pour les comparer.
Une solution naturelle serait d’ajouter la méthode statique Article.compare
:
article de classe { constructeur (titre, date) { this.title = titre ; this.date = date ; } comparaison statique (articleA, articleB) { renvoyer articleA.date - articleB.date ; } } // utilisation laissez les articles = [ nouvel article("HTML", nouvelle date(2019, 1, 1)), nouvel article("CSS", nouvelle date(2019, 0, 1)), nouvel article("JavaScript", nouvelle date(2019, 11, 1)) ]; articles.sort(Article.compare); alerte( articles[0].titre ); // CSS
Ici, la méthode Article.compare
se situe « au-dessus » des articles, comme moyen de les comparer. Il ne s'agit pas d'une méthode d'article, mais plutôt d'une classe entière.
Un autre exemple serait une méthode dite « d’usine ».
Disons que nous avons besoin de plusieurs façons de créer un article :
Créer selon les paramètres donnés ( title
, date
, etc.).
Créez un article vide avec la date du jour.
… ou bien d’une manière ou d’une autre.
La première méthode peut être implémentée par le constructeur. Et pour la seconde on peut créer une méthode statique de la classe.
Comme Article.createTodays()
ici :
article de classe { constructeur (titre, date) { this.title = titre ; this.date = date ; } statique createTodays() { // rappelez-vous, ceci = Article return new this("Résumé du jour", new Date()); } } let article = Article.createTodays(); alerte( article.titre ); // Le résumé du jour
Désormais, chaque fois que nous devons créer un résumé du jour, nous pouvons appeler Article.createTodays()
. Encore une fois, ce n'est pas une méthode d'article, mais une méthode de classe entière.
Les méthodes statiques sont également utilisées dans les classes liées aux bases de données pour rechercher/enregistrer/supprimer des entrées de la base de données, comme ceci :
// en supposant que Article est une classe spéciale pour gérer les articles // méthode statique pour supprimer l'article par identifiant : Article.remove({id : 12345});
Les méthodes statiques ne sont pas disponibles pour les objets individuels
Les méthodes statiques peuvent être appelées sur des classes et non sur des objets individuels.
Par exemple, un tel code ne fonctionnera pas :
//... article.createTodays(); /// Erreur : article.createTodays n'est pas une fonction
Un ajout récent
Il s'agit d'un ajout récent à la langue. Les exemples fonctionnent dans le Chrome récent.
Les propriétés statiques sont également possibles, elles ressemblent à des propriétés de classe normales, mais précédées de static
:
article de classe { éditeur statique = « Ilya Kantor » ; } alerte( Article.éditeur ); // Ilya Kantor
Cela équivaut à une affectation directe à Article
:
Article.publisher = "Ilya Kantor" ;
Les propriétés et méthodes statiques sont héritées.
Par exemple, Animal.compare
et Animal.planet
dans le code ci-dessous sont hérités et accessibles en tant que Rabbit.compare
et Rabbit.planet
:
classe Animal { planète statique = « Terre » ; constructeur (nom, vitesse) { this.speed = vitesse ; this.name = nom ; } courir (vitesse = 0) { this.speed += vitesse ; alert(`${this.name} s'exécute à la vitesse ${this.speed}.`); } comparaison statique (animalA, animalB) { retourner animalA.speed - animalB.speed; } } // Hériter d'Animal la classe Lapin étend Animal { cacher() { alert(`${this.name} se cache !`); } } laissez les lapins = [ nouveau Lapin("Lapin Blanc", 10), nouveau Lapin("Lapin Noir", 5) ]; lapins.sort(Lapin.compare); lapins[0].run(); // Black Rabbit court à la vitesse 5. alerte(Lapin.planète); // Terre
Désormais, lorsque nous appellerons Rabbit.compare
, le Animal.compare
hérité sera appelé.
Comment ça marche ? Encore une fois, en utilisant des prototypes. Comme vous l'avez peut-être déjà deviné, extends
donne Rabbit
la référence [[Prototype]]
à Animal
.
Ainsi, Rabbit extends Animal
crée deux références [[Prototype]]
:
La fonction Rabbit
hérite prototypiquement de la fonction Animal
.
Rabbit.prototype
hérite prototypiquement de Animal.prototype
.
Par conséquent, l’héritage fonctionne aussi bien pour les méthodes régulières que statiques.
Ici, vérifions cela par code :
classe Animal {} la classe Lapin étend Animal {} // pour la statique alert(Lapin.__proto__ === Animal); // vrai // pour les méthodes régulières alerte(Lapin.prototype.__proto__ === Animal.prototype); // vrai
Les méthodes statiques sont utilisées pour les fonctionnalités qui appartiennent à la classe « dans son ensemble ». Cela ne concerne pas une instance de classe concrète.
Par exemple, une méthode de comparaison Article.compare(article1, article2)
ou une méthode d'usine Article.createTodays()
.
Ils sont étiquetés par le mot static
dans la déclaration de classe.
Les propriétés statiques sont utilisées lorsque nous souhaitons stocker des données au niveau de la classe, également non liées à une instance.
La syntaxe est :
classe MaClasse { propriété statique = ... ; méthode statique() { ... } }
Techniquement, une déclaration statique revient à attribuer à la classe elle-même :
MaClasse.propriété = ... MaClasse.method = ...
Les propriétés et méthodes statiques sont héritées.
Pour class B extends A
le prototype de la classe B
lui-même pointe vers A
: B.[[Prototype]] = A
. Ainsi si un champ n'est pas trouvé dans B
, la recherche continue dans A
.
importance : 3
Comme nous le savons, tous les objets héritent normalement d' Object.prototype
et ont accès aux méthodes d'objet « génériques » comme hasOwnProperty
etc.
Par exemple:
classe Lapin { constructeur (nom) { this.name = nom ; } } let lapin = new Rabbit("Rab"); // La méthode hasOwnProperty provient de Object.prototype alert( lapin.hasOwnProperty('name') ); // vrai
Mais si nous l'épelons explicitement comme "class Rabbit extends Object"
, alors le résultat serait différent d'une simple "class Rabbit"
?
Quelle est la différence ?
Voici un exemple d'un tel code (cela ne fonctionne pas – pourquoi ? le réparer ?) :
la classe Rabbit étend l'objet { constructeur (nom) { this.name = nom ; } } let lapin = new Rabbit("Rab"); alert( lapin.hasOwnProperty('name') ); // Erreur
Voyons d’abord pourquoi ce dernier code ne fonctionne pas.
La raison devient évidente si nous essayons de l'exécuter. Un constructeur de classe héritant doit appeler super()
. Sinon, "this"
ne sera pas « défini ».
Voici donc le correctif :
la classe Rabbit étend l'objet { constructeur (nom) { super(); // besoin d'appeler le constructeur parent lors de l'héritage this.name = nom ; } } let lapin = new Rabbit("Rab"); alert( lapin.hasOwnProperty('name') ); // vrai
Mais ce n'est pas encore tout.
Même après le correctif, il existe toujours une différence importante entre "class Rabbit extends Object"
et class Rabbit
.
Comme nous le savons, la syntaxe « extends » met en place deux prototypes :
Entre "prototype"
des fonctions constructeur (pour les méthodes).
Entre les fonctions constructeur elles-mêmes (pour les méthodes statiques).
Dans le cas de class Rabbit extends Object
cela signifie :
la classe Rabbit étend l'objet {} alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) vrai alert( Rabbit.__proto__ === Objet ); // (2) vrai
Ainsi Rabbit
donne désormais accès aux méthodes statiques de Object
via Rabbit
, comme ceci :
la classe Rabbit étend l'objet {} // normalement nous appelons Object.getOwnPropertyNames alerte ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); //a,b
Mais si nous n’avons pas extends Object
, alors Rabbit.__proto__
n’est pas défini sur Object
.
Voici la démo :
classe Lapin {} alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) vrai alert( Rabbit.__proto__ === Objet ); // (2) faux (!) alert( Rabbit.__proto__ === Function.prototype ); // comme n'importe quelle fonction par défaut // erreur, aucune fonction de ce type dans Rabbit alerte ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Erreur
Rabbit
ne donne donc pas accès aux méthodes statiques d' Object
dans ce cas.
À propos, Function.prototype
a également des méthodes de fonction « génériques », comme call
, bind
etc. Elles sont finalement disponibles dans les deux cas, car pour le constructeur Object
intégré, Object.__proto__ === Function.prototype
.
Voici la photo :
Donc, pour faire court, il y a deux différences :
Lapin de classe | la classe Rabbit étend l'objet |
---|---|
– | doit appeler super() dans le constructeur |
Rabbit.__proto__ === Function.prototype | Rabbit.__proto__ === Object |