Les objets sont généralement créés pour représenter des entités du monde réel, comme des utilisateurs, des commandes, etc. :
laissez l'utilisateur = { nom : "Jean", âge : 30 ans } ;
Et, dans le monde réel, un utilisateur peut agir : sélectionner quelque chose dans le panier, se connecter, se déconnecter, etc.
Les actions sont représentées en JavaScript par des fonctions dans les propriétés.
Pour commencer, apprenons à l' user
à dire bonjour :
laissez l'utilisateur = { nom : "Jean", âge : 30 ans } ; user.sayHi = fonction() { alert("Bonjour !"); } ; user.sayHi(); // Bonjour!
Ici, nous venons d'utiliser une expression de fonction pour créer une fonction et l'attribuer à la propriété user.sayHi
de l'objet.
Ensuite, nous pouvons l'appeler user.sayHi()
. L'utilisateur peut désormais parler !
Une fonction qui est une propriété d'un objet est appelée sa méthode .
Nous avons donc ici une méthode sayHi
de l' user
de l'objet.
Bien sûr, nous pourrions utiliser une fonction pré-déclarée comme méthode, comme ceci :
laissez l'utilisateur = { //... } ; // d'abord, déclare fonction direSalut() { alert("Bonjour !"); } // puis ajoute comme méthode user.sayHi = sayHi; user.sayHi(); // Bonjour!
Programmation orientée objet
Lorsque nous écrivons notre code en utilisant des objets pour représenter des entités, c'est ce qu'on appelle la programmation orientée objet, en abrégé : « POO ».
La POO est une grande chose, une science intéressante en soi. Comment choisir les bonnes entités ? Comment organiser l’interaction entre eux ? C'est ça l'architecture, et il existe d'excellents livres sur ce sujet, comme « Design Patterns: Elements of Realistic Object-Oriented Software » de E. Gamma, R. Helm, R. Johnson, J. Vissides ou « Object-Oriented Analysis and Design with Applications » de G. Booch, et plus encore.
Il existe une syntaxe plus courte pour les méthodes dans un objet littéral :
// ces objets font la même chose utilisateur = { disBonjour : function() { alert("Bonjour"); } } ; // Le raccourci de la méthode semble meilleur, n'est-ce pas ? utilisateur = { sayHi() { // identique à "sayHi: function(){...}" alert("Bonjour"); } } ;
Comme démontré, nous pouvons omettre "function"
et simplement écrire sayHi()
.
A vrai dire, les notations ne sont pas tout à fait identiques. Il existe des différences subtiles liées à l'héritage des objets (qui seront abordées plus tard), mais pour l'instant elles n'ont pas d'importance. Dans presque tous les cas, la syntaxe la plus courte est préférée.
Il est courant qu'une méthode objet doive accéder aux informations stockées dans l'objet pour faire son travail.
Par exemple, le code à l'intérieur user.sayHi()
peut avoir besoin du nom de l' user
.
Pour accéder à l'objet, une méthode peut utiliser le mot-clé this
.
La valeur de this
est l'objet « avant le point », celui utilisé pour appeler la méthode.
Par exemple:
laissez l'utilisateur = { nom : "Jean", âge : 30 ans, disBonjour() { // "ceci" est "l'objet actuel" alert(ce.nom); } } ; user.sayHi(); // John
Ici, lors de l'exécution de user.sayHi()
, la valeur de this
sera user
.
Techniquement, il est également possible d'accéder à l'objet sans this
, en le référençant via la variable externe :
laissez l'utilisateur = { nom : "Jean", âge : 30 ans, disBonjour() { alert(utilisateur.nom); // "utilisateur" au lieu de "ceci" } } ;
… Mais un tel code n'est pas fiable. Si nous décidons de copier user
dans une autre variable, par exemple admin = user
et d'écraser user
par autre chose, alors il accédera au mauvais objet.
Cela est démontré ci-dessous :
laissez l'utilisateur = { nom : "Jean", âge : 30 ans, disBonjour() { alert( utilisateur.nom ); // conduit à une erreur } } ; laissez admin = utilisateur ; utilisateur = nul ; // écrase pour rendre les choses évidentes admin.sayHi(); // TypeError : Impossible de lire la propriété 'nom' de null
Si nous utilisions this.name
au lieu de user.name
dans l' alert
, alors le code fonctionnerait.
En JavaScript, le mot-clé this
se comporte contrairement à la plupart des autres langages de programmation. Il peut être utilisé dans n’importe quelle fonction, même s’il ne s’agit pas d’une méthode d’objet.
Il n'y a aucune erreur de syntaxe dans l'exemple suivant :
fonction direSalut() { alert( this.name ); }
La valeur de this
est évaluée pendant l'exécution, en fonction du contexte.
Par exemple, ici, la même fonction est affectée à deux objets différents et a des « ceci » différents dans les appels :
let user = { nom : "John" } ; let admin = { nom : "Admin" } ; fonction direSalut() { alert( this.name ); } // utilise la même fonction dans deux objets user.f = sayHi; admin.f = direSalut ; // ces appels ont des valeurs différentes // "ceci" à l'intérieur de la fonction est l'objet "avant le point" utilisateur.f(); // John (ce == utilisateur) admin.f(); // Administrateur (ceci == administrateur) admin['f'](); // Administrateur (les points ou les crochets accèdent à la méthode – n'a pas d'importance)
La règle est simple : si obj.f()
est appelé, alors this
obj
lors de l'appel de f
. Il s'agit donc soit d' user
, soit admin
dans l'exemple ci-dessus.
Appel sans objet : this == undefined
Nous pouvons même appeler la fonction sans aucun objet :
fonction direSalut() { alerte(ce); } disBonjour(); // non défini
Dans ce cas, this
undefined
en mode strict. Si nous essayons d'accéder this.name
, il y aura une erreur.
En mode non strict, la valeur de this
sera dans ce cas l' objet global ( window
dans un navigateur, nous y reviendrons plus tard dans le chapitre Objet global). Il s'agit d'un comportement historique qui "use strict"
.
Habituellement, un tel appel est une erreur de programmation. S'il y a this
dans une fonction, elle s'attend à être appelée dans un contexte d'objet.
Les conséquences de this
déliaison
Si vous venez d'un autre langage de programmation, alors vous êtes probablement habitué à l'idée d'un « this
lié », où les méthodes définies dans un objet ont toujours this
faisant référence à cet objet.
En JavaScript, this
« gratuit », sa valeur est évaluée au moment de l'appel et ne dépend pas de l'endroit où la méthode a été déclarée, mais plutôt de l'objet qui se trouve « avant le point ».
Le concept d'exécution évalué this
présente à la fois des avantages et des inconvénients. D’une part, une fonction peut être réutilisée pour différents objets. D’un autre côté, une plus grande flexibilité crée davantage de possibilités d’erreurs.
Ici, notre position n'est pas de juger si cette décision de conception de langage est bonne ou mauvaise. Nous comprendrons comment travailler avec, comment obtenir des avantages et éviter les problèmes.
Les fonctions fléchées sont particulières : elles n'ont pas leur « propre » this
. Si nous faisons référence this
à partir d’une telle fonction, cela provient de la fonction « normale » externe.
Par exemple, ici arrow()
utilise this
à partir de la méthode externe user.sayHi()
:
laissez l'utilisateur = { prénom: "Ilya", disBonjour() { let arrow = () => alert(this.firstName); flèche(); } } ; user.sayHi(); // Ilya
C'est une particularité des fonctions fléchées, c'est utile lorsque nous ne voulons pas avoir un this
séparé, mais plutôt le sortir du contexte extérieur. Plus loin dans le chapitre Fonctions fléchées revisitées, nous approfondirons les fonctions fléchées.
Les fonctions stockées dans les propriétés de l'objet sont appelées « méthodes ».
Les méthodes permettent aux objets « d’agir » comme object.doSomething()
.
Les méthodes peuvent référencer l'objet comme this
.
La valeur de this
est définie au moment de l'exécution.
Lorsqu'une fonction est déclarée, elle peut utiliser this
, mais this
n'a aucune valeur jusqu'à ce que la fonction soit appelée.
Une fonction peut être copiée entre des objets.
Lorsqu'une fonction est appelée dans la syntaxe « méthode » : object.method()
, la valeur de this
lors de l'appel est object
.
Veuillez noter que les fonctions fléchées sont particulières : elles n'ont pas this
. Lorsque this
on y accède à l’intérieur d’une fonction fléchée, il est extrait de l’extérieur.
importance : 5
Ici, la fonction makeUser
renvoie un objet.
Quel est le résultat de l'accès à sa ref
? Pourquoi?
fonction makeUser() { retour { nom : "Jean", réf : ceci } ; } laissez l'utilisateur = makeUser(); alerte( user.ref.name ); // Quel est le résultat ?
Réponse : une erreur.
Essayez-le :
fonction makeUser() { retour { nom : "Jean", réf : ceci } ; } laissez l'utilisateur = makeUser(); alerte( user.ref.name ); // Erreur : Impossible de lire la propriété 'nom' non définie
C'est parce que les règles qui définissent this
ne tiennent pas compte de la définition de l'objet. Seul le moment de l’appel compte.
Ici, la valeur de this
à l'intérieur makeUser()
est undefined
, car elle est appelée en tant que fonction, et non en tant que méthode avec une syntaxe « point ».
La valeur de this
est une pour l'ensemble de la fonction, les blocs de code et les littéraux d'objet ne l'affectent pas.
Donc ref: this
prend en fait le this
de la fonction.
Nous pouvons réécrire la fonction et renvoyer la même this
avec une valeur undefined
:
fonction makeUser(){ rends ceci ; // cette fois, il n'y a pas de littéral d'objet } alert( makeUser().name ); // Erreur : Impossible de lire la propriété 'nom' non définie
Comme vous pouvez le voir, le résultat de alert( makeUser().name )
est le même que le résultat de alert( user.ref.name )
de l'exemple précédent.
Voici le cas inverse :
fonction makeUser() { retour { nom : "Jean", réf() { rends ceci ; } } ; } laissez l'utilisateur = makeUser(); alerte( user.ref().name ); // John
Maintenant, cela fonctionne, car user.ref()
est une méthode. Et la valeur de this
est définie sur l'objet avant le point .
.
importance : 5
Créez un calculator
d'objets avec trois méthodes :
read()
demande deux valeurs et les enregistre en tant que propriétés d'objet avec les noms a
et b
respectivement.
sum()
renvoie la somme des valeurs enregistrées.
mul()
multiplie les valeurs enregistrées et renvoie le résultat.
soit la calculatrice = { //... votre code... } ; calculatrice.read(); alert( calculatrice.sum() ); alert( calculatrice.mul() );
Exécutez la démo
Ouvrez un bac à sable avec des tests.
soit la calculatrice = { somme() { renvoie ceci.a + ceci.b; }, mul() { renvoie ceci.a * ceci.b; }, lire() { this.a = +prompt('a?', 0); this.b = +prompt('b?', 0); } } ; calculatrice.read(); alert( calculatrice.sum() ); alert( calculatrice.mul() );
Ouvrez la solution avec des tests dans un bac à sable.
importance : 2
Il y a un objet ladder
qui vous permet de monter et de descendre :
laissez échelle = { pas: 0, en haut() { this.step++; }, vers le bas() { cette.étape--; }, showStep : function() { // affiche l'étape en cours alert( this.step ); } } ;
Maintenant, si nous devons faire plusieurs appels en séquence, nous pouvons procéder comme ceci :
échelle.up(); échelle.up(); échelle.down(); échelle.showStep(); // 1 échelle.down(); échelle.showStep(); // 0
Modifiez le code de up
, down
et showStep
pour rendre les appels chaînables, comme ceci :
ladder.up().up().down().showStep().down().showStep(); // affiche 1 puis 0
Une telle approche est largement utilisée dans les bibliothèques JavaScript.
Ouvrez un bac à sable avec des tests.
La solution est de renvoyer l'objet lui-même à chaque appel.
laissez échelle = { pas: 0, en haut() { this.step++; rends ceci ; }, vers le bas() { cette.étape--; rends ceci ; }, showStep() { alert( this.step ); rends ceci ; } } ; ladder.up().up().down().showStep().down().showStep(); // affiche 1 puis 0
Nous pouvons également rédiger un seul appel par ligne. Pour les longues chaînes, c'est plus lisible :
échelle .en haut() .en haut() .vers le bas() .showStep() // 1 .vers le bas() .showStep(); // 0
Ouvrez la solution avec des tests dans un bac à sable.