open

Les objets en JavaScript

#1 Qu'est ce qu'un objet ? open

On va prendre un exemple concret. Supposons que l'on veuille gérer une boutique en ligne d'articles de sport. Tous les articles vont avoir des caractéristiques communes. Ils vont tous avoir un nom de produit, ils vont tous avoir une référence et aussi un prix hors taxes. Ils auront aussi une fonctionnalité commune car pour tous les articles on aura besoin de calculer le prix toutes taxes comprises.

  1. Au niveau du code que l'on va écrire pour gérer cette boutique on voit bien que pour chaque article on va avoir besoin d'une variable produit pour stocker le nom du produit, d'une autre variable reference pour stocker la référence et d'une dernière variable prixHT pour stocker le prix hors taxes.
  2. Ces variables sont liées entre elles car elles correspondent à un article. Et ces variables ont une valeur spécifique pour chacun de ces articles.
  3. La première idée c'est de regrouper ces variables et de les encapsuler dans une "boîte". Cette boite on va lui donner un nom et pour ça on va se servir d'une variable. On va l'appeler article1. Déjà je peux le dire cette boîte c'est un objet. Ci-dessous je vous montre sur un schéma ce que sera l'objet article1 utilisé pour un "Sac à dos".
    Un premier objet article1
    Le schéma d'un premier objet article1
  4. Si maintenant je veux fabriquer un objet pour des "Skis de rando" et bien je vais avoir besoin d'une deuxième boîte. Cette fois je vais utiliser la variable article2 et je vais refaire la même chose cet à dire avoir besoin dans la boite d'une variable pour le nom du produit, d'une autre pour la référence... Ci-dessous je vous montre avec un schéma ce que sera l'objet article2.
    Un deuxième objet article2
    Le schéma d'un deuxième objet article2
  5. Deux remarques :
    • Vous imaginez facilement que si on fabriquait un objet pour une boutique réelle il aurait beaucoup plus variables car il y aurait beaucoup plus de caractéristiques.
    • Et je peux ajouter que pour une boutique réelle on aurait probablement besoin de beaucoup d'articles.

    Du coup la deuxième idée va être de fabriquer une "boîte modèle" et de l'utiliser pour construire chacun de nos articles.

    Dans notre exemple cette "boîte modèle" on va l'appeler Article. Vous voyez sur le schéma ci-dessous que les variables dans cette "boîte modèle" ne sont pas initialisées. Cette boîte doit aussi contenir la fonction calculerPrixTTC.

    Un modèle pour construire nos objets
    Le schéma d'un modèle

    Etant donné que l'on se sert de cette "boîte modèle" pour construire les objets. Cette "boîte modèle" on l'a appelé le constructeur. Et maintenant on ne va plus parler de "boîte modèle" mais on va parler de constructeur.

    Un constructeur

    Maintenant avant de partir tête baissée dans le code on va regarder ce que doit faire un constructeur pour construire un nouvel objet par exemple article3. Pour ce nouvel objet il va allouer de la mémoire puis il va regarder la liste des variables qu'on lui donné et dans cette mémoire il va implanter la même liste de variables. Ensuite il faudra qu'il initialise chacune de ces variables avec la valeur spécifique à ce nouvel objet.

  6. Au niveau vocabulaire: les variables produit, reference, prixHT s'appellent les propriétés d'un objet et la fonction calculerPrixTTC s'appelle une méthode d'un objet. Ici il n'y a qu'une seule méthode mais bien sûr il pourrait y en avoir plusieurs.
  7. Pour mettre en oeuvre notre constructeur en JavaScript on a plusieurs solutions. On peut utiliser une classe ou une fonction constructeur et dans la suite du tuto on va poursuivre l'exemple en utilisant une classe.

#2 Un point de vocabulaire pour ne pas s'embrouiller open

Alors pourquoi pourrait-on s'embrouiller ? Et bien parce que l'on peut dire les mêmes choses de plusieurs façons différentes.

  1. De manière générale on construit les objets avec un constructeur. C'est ce qu'il faut retenir.
  2. Si on utilise une classe pour le constructeur le plus souvent on dit que l'on instancie la classe. A ce moment là on parle d'instance au lieu de parler d'objet. Mais une instance et un objet c'est strictement la même chose. C'est juste une autre façon de le dire.
  3. Si on utilise une fonction constructeur il m'est arrivé de lire que l'on instancie un nouvel objet à partir la fonction constructeur. C'est même une expression que j'ai trouvé dans la doc MDN.

Ne vous laissez pas embrouiller. Instancier moi je le traduis par "prendre un exemplaire". Par exemple lorsque l'on dit que l'on prend une instance d'une classe on peut dire que l'on prend un exemplaire de la classe. Lorsque on dit que l'on instancie un nouvel objet à partir d'une fonction constructeur on peut dire que l'on prend un nouvel exemplaire d'un objet à partir d'une fonction constructeur. Ce qui est important c'est de ne pas perdre de vue qu'il y a d'un côté le constructeur et de l'autre les objets.

Un vocabulaire pas toujours évident
Un point de vocabulaire

#3 La déclaration d'une classe et la construction des objets open

Maintenant je vais coder le constructeur. Je vais le faire en utilisant les classes JavaScript qui sont disponibles depuis la version ECMAScript 6 soit l'édition de 2015.

Je vais prendre Sublime Text et créer sur le bureau un fichier que je vais appeler Article.html. Je fais !>script puis tab et je donne le titre Article à la page web.

Maintenant, je fais Open in Browser dans Sublime de manière à avoir sous les yeux le code et son exécution.

  1. Qu'est ce que j'ai fait dans le code ci-dessus ? En premier j'ai déclaré la classe Article.Donc ça c'est le constructeur. Comprenez bien que c'est une déclaration. C'est comme une déclaration d'une grosse fonction. Ensuite j'ai appelé la fonction en faisant le new Article(). J'ai construit un premier objet article1 en utilisant les valeurs spécifiques à article1 (* 'Sac à dos'...). Ensuite j'ai construit un deuxième objet article2 en instanciant une nouvelle fois la classe mais cette fois ci en utilisant les valeurs spécifiques à article2 (* 'Ski de rando'...).
  2. Pour déclarer la classe Article, je dois utiliser le mot réservé class et le faire suivre de Article. L'usage veut que l'on mette une majuscule à Article qui signale que c'est un nom de classe. En fait cet usage vient des fonctions constructeurs. La majuscule permettant dans ce cas de différencier une fonction constructeur d'une fonction normale. On a gardé cet usage avec les classes.
  3. Ensuite j'ai déclaré une fonction qui doit s'appeler constructor (constructeur en français). Donc dans la classe il y a une fonction qui va faire ce travail de construction et c'est celle là. Notez que je n'ai pas eu besoin d'utiliser le mot réservé function.
  4. Cette fonction constructor est appelée au moment du new Article(). C'est le moteur JavaScript qui l'appelle ce n'est pas le programmeur. Le programmeur déclenche l'appel de constructor il ne l'appelle pas directement. Et c'est les arguments qui sont passés ici à Article() qui vont être passés à la fonction constructor.
  5. La fonction constructor va allouer l'objet en mémoire. Le mot réservé this est un pointeur qui va pointer sur la zone mémoire allouée pour ce nouvel objet. Un console.log de typeof this affiche object qui est du type pointeur sur un objet. Et un console.log de this affiche l'objet qui est en cours de construction. On peut remarquer que le console.log prend un pointeur en argument et qu'il affiche l'objet pointé et non le pointeur. Pour ceux qui on fait du langage C ici le console.log déréférence le pointeur.
  6. La fonction constructor va initialiser les propriétés du nouvel l'objet avec les valeurs spécifiques du nouvel l'objet. C'est ce qui se fait par exemple ici pour la propriété produit this.produit = produit. Pour pouvoir faire ça il faut que les valeurs du nouvel objet soient en paramètre de la fonction constructor. Je vais avoir besoin d'une valeur pour chaque propriété à initialiser, donc d'un paramètre pour chaque propriété. Et en général on donne le même nom au paramètre et à la propriété.
    Initialisation de l'objet
  7. Notez bien que les variables article1 et article2 reçoivent chacune la valeur d'un pointeur. C'est une valeur retournée par le new Article(). La valeur du this. C'est comme ça que les variables article1 et article2 récupère l'adresse de l'objet en mémoire.
  8. On peut le vérifier avec la console. Je fais un typeof article1 et je lis object. Donc article1 est du type pointeur sur objet.
  9. Maintenant je tape article1, je déploie, je peux visualiser les trois propriétés produit reference et prixHT.
Tester le code
Role du this

Tout ça c'est bien mais où est passé la méthode calculerPrixTTC ? Alors la réponse je vais la donner dans le chapitre ci-dessous.

#4 Qu'est-ce qu'un prototype ? open

  1. Je clique sur la propriété __proto__ et je vois apparaître la méthode calculerPrixTTC().
    Examiner la propriété __proto__
  2. Maintenant je tape article2 puis je déploie Article puis __proto__ et ici aussi je vois apparaître la méthode calculerPrixTTC().
    Où est la méthode calculerPrixTTC ?
  3. A ce stade on pourrait croire que chaque objet a sa propre méthode. Heureusement ce n'est pas le cas. Imaginez qu'il y ait 1000 objets !! Ce serait idiot d'implanter 1000 fois la même méthode en mémoire.
  4. En fait la méthode a été placée dans ce que l'on appelle un prototype. Il s'agit d'un objet au sens JavaScript du terme c'est à dire qu'en interne le prototype est implémenté par un objet. Dans notre exemple c'est le prototype de tous les objets de la classe Article. Il y en a qu'un seul et on va le vérifier.
    article1 et article2 partagent le prototype des objets de la classe Article
    article1 et article2 partage le proto d'Article
  5. Faites typeof article1.__proto__ et vous obtenez object.
  6. Faites typeof article2.__proto__ et vous obtenez object.
  7. Et si maintenant vous testez l'égalité de ces deux __proto__ et bien vous voyez qu'ils sont égaux c'est à dire qu'ils pointent sur le même objet.
    Les deux pointeurs __proto__ des objets sont égaux
    égalité des __proto__ testée sur la console

Donc tous les objets construits avec la classe Article vont partager ce prototype et il faut remarquer que c'est de cette manière qu'ils vont partager le code de la méthode. Le code de la méthode n'est pas dupliqué.

Cependant si vous regardez dans le premier __proto__ vous constatez qu'il contient un autre __proto__ qui lui pointe sur le prototype d'un Object. Ensuite ça s'arrête. J'ai mis un point d'interrogation sur le schéma car on va voir à quoi ça correspond dans la suite du tuto.

#5 Comment se servir d'une classe parent pour créer une classe enfant open

Il y a une deuxième façon d'utiliser la "boîte modèle". Pour créer une nouvelle classe plutôt que de partir d'une boîte vide on va récupérer une classe qui existe déjà. Dans la nouvelle classe on va juste écrire les propriétés et les méthodes que l'on ajoute. La nouvelle classe est appelée classe enfant ou classe dérivée et la classe récupérée est appelée la classe parent. Ce mécanisme qui permet de récupérer des propriétés et des méthodes existantes s'appelle l'héritage en Programmation Orientée Objet (POO).

Il faut bien comprendre que les objets enfants que l'on construira auront les propriétés et méthodes spécifiques à la classe enfant PLUS les propriétés et méthodes de la classe parent. C'est à dire les propriétés et méthodes que la classe enfant hérite de la classe parent.

Construire une classe enfant à partir d'une classe parent
Construire une classe par héritage

Poursuivons notre exemple. Supposons que l'on veuille mettre des articles en promotion dans notre boutique en ligne. Ces articles ont les mêmes caractéristiques que les autres articles à savoir qu'ils ont toujours un nom de produit, une référence et un prixHT. Mais en plus ils ont une propriété supplémentaire qui est la remise que l'on va faire sur le PrixTTC. Et aussi une méthode en plus pour calculer le prix de vente en promotion.

Nous ce qu'on veut c'est pas tout recommencer, alors on va se servir de la classe Article.

Etant donné que nous avons à notre disposition en JavaScript un mécanisme d'héritage on va récupérer la classe Article pour construire une nouvelle classe Promo. Et pour Promo on va se contenter d'ajouter une propriété remise et une méthode calculerPrixPromo().

Construire la classe Promo à partir de la classe Article
Construire Promo à partir de Article

Je vais sauvegarder le fichier Article.html sous un nouveau nom Promo.html. Je vais travailler maintenant sur Promo.html.

  1. Je déclare la classe Promo comme une extension de la classe Article. Je dois utiliser le mot réservé extends.
  2. Maintenant je dois déclarer un constructeur pour la classe Promo. Ce constructeur doit prendre comme paramètres de déclaration l'ensemble des propriétés des deux classes puisque les propriétés des deux classes doivent être affectées au moment de l'instanciation. Les propriétés produit, reference et prixHT de la classe parent et la propriété remise de la classe dérivée.
  3. Dans ce constructeur, je dois appeler le constructeur de la classe parent en utilisant le mot réservé super. Et je passe les paramètres de déclaration du parent.
  4. Ensuite je dois initialiser la propriété de la classe enfant. Ici il n'y a que remise. J'utilise à nouveau this qui pointe ici toujours sur un nouvel objet mais cette fois ci un objet de la classe Promo.
  5. Ensuite je passe au codage de la méthode calculerPrixPromo. Pour cela j'ai besoin d'accéder à la méthode calculerPrixTTC de la classe parent et pour y accéder je dois à nouveau utiliser le mot réservé super. Mais cette fois ci je dois utiliser la notation pointée.
  6. Ensuite je peux instancier Promo en prenant bien soin de mettre tous les paramètres d'appel.
Tester le code

#6 Héritage par prototype open

  1. Reprenez le fichier Promo.html, avec la console examinez l'objet promo.
  2. Vous constatez que le prototype de Promo semble "contenir" celui d'Article. En fait c'est le pointeur __proto__ du prototype de promo qui pointe vers celui d'Article.
le prototype de Promo
S'y retrouver dans les infos console

Ca veut dire qu'un prototype contient un pointeur vers un autre prototype. On a donc une liste chaînée de prototypes qui se termine par le prototype de l'objet Object.

Liste chaînée de prototypes
Liste chaînée de prototype

Pour le vérifier on va prendre l'objet promo et on va aller sur son prototype puis on va descendre sur sa propriété __proto__. On va comparer cette valeur à celle de article1.__proto__. On va constater que les valeurs sont égales ce qui prouve le chaînage des prototypes.

Pointeurs en rouge sur le schéma ci-dessus
Preuve du chaînage des prototypes

Je résume le fonctionnement qui est très simple : si une propriété est demandée, l'interpréteur regarde d'abord dans l'objet. Si elle n'est pas dans l'objet il la cherche dans le prototype de l'objet. Si elle n'y est pas il remonte au prototype du parent et ainsi de suite jusqu'au prototype de Object. Le prototype de Object est le prototype de l'objet qui est en haut de la hiérarchie.

#7 Comment créer un objet littéral ? open

C'est une autre façon de procéder pour créer un objet. Dans ce cas, le plus souvent, on commence par créer un objet vide en utilisant les accolades. Puis on ajoute ou on supprime des propriétés de manière dynamique. JavaScript a cette capacité de nous permettre d'ajouter ou de supprimer des propriétés au fur et à mesure de notre besoin.

Pour ajouter une propriété il suffit d'affecter une nouvelle propriété.

Pour supprimer une propriété il faut utiliser l'opérateur delete.

  1. Avec Sublime faites CTRL+N pour créer un nouveau fichier puis CTRL+S pour le sauvegarder sur le bureau. Vous lui donnez le nom de Litteral.html. Faites !>script puis tab. Donnez le titre Litteral à la page web.
  2. Déclarez un objet vide article en utilisant les accolades.
  3. Ajoutez la propriété produit en affectant article.produit à 'Sac à dos'.
  4. Ajoutez la propriété reference en affectant article.reference à 645020.
  5. Ajouter la propriété prixHT en affectant article.prixHT à 35.
  6. Ajouter la propriété stock en affectant article.stock à 10.
  7. Supprimez la propriété reference avec un delete sur article.reference.
  8. Faites un console.log() pour afficher l'objet.
  9. Faites un clic droit dans Sublime puis choisissez Open in Browser. Puis CTRL+SHIFT+I pour accéder à la console.
  10. Vérifiez la liste des propriétés.
Tester le code

Vous pouvez aussi utiliser la syntaxe JSON pour créer un objet avec ses propriétés initialisées. Chaque propriété est composée d'un couple nom et valeur. Le nom et la valeur sont séparés par le caractère deux points. Chaque couple nom et valeur est séparé par une virgule.

  1. Effacez ce qu'il y a dans l'élément script de Litteral.html sauf le console.log().
  2. Déclarez un objet en utilisant la syntaxe JSON.
  3. Faites un clic droit dans Sublime puis choisissez Open in Browser. Puis CTRL+SHIFT+I pour accéder à la console.
  4. Vérifiez la liste des propriétés.
Tester le code

#8 Tester si une propriété existe dans un objet open

On peut se poser deux questions :

  1. Est ce que la propriété existe que ce soit dans l'objet ou dans sa chaîne de prototype ? C'est l'opérateur in qui répond à cette question. Il vous renvoie true si la réponse à la question est affirmative.
  2. Est ce que la propriété existe dans l'objet ? C'est la méthode hasOwnProperty() qui répond à cette question. Elle renvoie true si la propriété se trouve dans l'objet. Elle renvoie false si la propriété se trouve dans la chaîne de prototype, y compris le prototype de l'objet lui même.

On va tester ces deux moyens d'abord sur la propriété remise et sur la méthode calculerPrixPromo() qui appartiennent à l'objet Promo, puis sur la propriété valueOf qui elle appartient à Object.

Tester le code

#9 Parcourir la liste des propriétés open

Tester le code

#10 Passer un objet en paramètre d'appel d'une fonction open

Reprenez le fichier Article.html.

On a vu que article1 est un pointeur. Vous allez passer ce pointeur à une fonction. Le pointeur va être passé par copie comme expliqué dans le tuto sur les fonctions. Mais ce pointeur permettra un accès à l'objet et permettra une modification des propriétés de l'objet depuis la fonction.

Bien sûr article1 est globale et on peut y accéder dans la fonction modifier mais ce n'est pas ce que je veux montrer.

un pointeur en paramètre d'appel
Un pointeur en paramètre d'appel
Tester le code