Logo JavaScript

Notion d’objets en Javascript

Flattr this!

Je me rends compte avec le temps que le développement objet en Javascript laisse souvent perplexes de très nombreux développeurs. C'est pourquoi je me lance dans une tentative d'explication pour y mettre un peu de lumière. Nous y verrons d'abord la notion de classe, la création d'objets puis quelques propos autour des éléments statiques et enfin j'aborderai très brièvement l'héritage.

La notion de classe

Tout d'abord, il n'y a pas de notion de "classe" en Javascript. Tout objet est lié à ce qu'on appelle un prototype et c'est ce dernier qui permet de savoir quelles méthodes sont utilisables sur un objet.

Vous pouvez créer un objet en vous basant sur le prototype d'un autre. Un objet Javascript n'est donc jamais une instance de classe comme dans les langages objets classiques (je pense aux Java, PHP, .Net, C++ et autres) mais une instance d'un autre objet. De plus, il ne peut exister de prototype sans objet et inversement.

Les propriétés d'un objet lui sont propres et ne dépendent pas du prototype. Vous pouvez avoir deux objets avec le même prototype mais des propriétés totalement différentes, que ce soit par leurs valeurs, leurs noms ou leur nombre. Ce n'est pas de la magie, c'est du Javascript.

Comment créer un objet ?

C'est très simple. Il y a 2 méthodes : soit via un objet littéral, soit via une méthode qui sera alors considérée comme le constructeur de cet objet.

Voyons le code suivant :

var monLitteral = {};

var objetDeBase = new Object();

function constructeurFictif() {}

var monObjet = new constructeurFictif();
var monObjetBis = new monObjet.constructor();

Dans un premier temps, je créé un objet dit "littéral", c'est à dire qu'il sera instancié tel que décrit. Voilà comment un débogueur vous le présentera :

  • monLitteral: Object
    • __proto__: Object
      • __defineGetter__: function __defineGetter__() { [native code] }
      • __defineSetter__: function __defineSetter__() { [native code] }
      • __lookupGetter__: function __lookupGetter__() { [native code] }
      • __lookupSetter__: function __lookupSetter__() { [native code] }
      • constructor: function Object() { [native code] }
      • hasOwnProperty: function hasOwnProperty() { [native code] }
      • isPrototypeOf: function isPrototypeOf() { [native code] }
      • propertyIsEnumerable: function propertyIsEnumerable() { [native code] }
      • toLocaleString: function toLocaleString() { [native code] }
      • toString: function toString() { [native code] }
      • valueOf: function valueOf() { [native code] }

Il n'a donc aucune propriété et ne contient que les méthodes par défaut de tout objet Javascript. Il existe en effet des méthodes créées par défaut pour tout objet. Encore une spécificité de ce langage méconnu de nombreux développeurs.

objetDeBase, lui, est directement créé à partir de la méthode Object(). Cela permet d'obtenir un objet complètement "nu", sans propriété et avec les seules méthodes par défaut.

Du côté de monObjet et monOBjetBis, nous avons d'hors et déjà créé des objets "enrichis". J'ai défini une méthode constructeurFictif() qui ne fait rien du tout. Elle pourrait tout aussi bien vous commander une pizza ou faire tourner une application de gestion de vos clients là n'est pas la question. Une fonction, peu importe son corps, suffit à être utilisable comme constructeur.

Vous pouvez donc réutiliser le constructeur d'un objet pour instancier un objet si vous ne voulez/pouvez pas rappeler la fonction d'origine, même si c'est quand même plus facile. Le mot clé réservé constructor est disponible pour tout objet, y compris les objets littéraux, qui eux utilisent implicitement Object() comme constructeur.

Comment définir des propriétés statiques ?

Vous ne pouvez pas. C'est aussi simple que ça. Il existe bien un mot clé static qui est réservé mais il ne sert à rien.

Comment définir des méthodes statiques ?

Comme les propriétés statiques, c'est impossible.

Comment faire de l'héritage en Javascript ?

C'est particulièrement simple. Essayons ça :

function constructeurFictif() {}

var monObjet = new constructeurFictif();
constructeurFictif.prototype.hello = function() {
	alert('hello world!');
}
var monObjetBis = new constructeurFictif();

Si vous inspectez vos variables avec un débogueur, vous aurez le même prototype pour les deux objets même si la déclaration de monObjet précède celle de la fonction hello(). Fonction qu'on a bêtement rajouté au prototype du constructeurFictif(). Toute fonction étant aussi un objet, rien ne vous interdit donc de modifier le prototype d'une fonction finalement.

Il existe d'autres méthodologies mais celle là est déjà une première approche même si c'est loin d'être la meilleure. Je ferai un topo plus complet à l'occasion sur ce point.

Conclusion

Je vous propose une liste memento des points clés à retenir de ce billet :

  • Il n'existe pas de classe en Javascript ;
  • Un objet a des propriétés qui lui sont propres ;
  • Un prototype est une "liste" des méthodes appartenant à l'objet mais contient aussi une référence à son constructeur ;
  • Un objet possède son propre prototype ;
  • Tout prototype a aussi son propre prototype ;
  • Le prototype père correspond à la fonction Object() qui a elle même pour constructeur Empty() qui a pour constructeur Object() et ainsi de suite, vous ne pourrez remonter plus haut ;
  • Il n'est pas possible de définir des éléments statiques, que ce soit des méthodes ou des propriétés ;
  • Un objet hérite du prototype de son constructeur.

Je vous ferai un autre billet bientôt pour rentrer plus dans le détail de l'héritage en Javascript.

Flattr this!

A propos de Mathieu

Ingénieur développeur web dans la vente par correspondance B2B, adepte de nouvelles technologies et d'innovation. Vous pouvez aussi me retrouver sur Twitter @mathrobin
Cette entrée a été publiée dans JavaScript, avec comme mot(s)-clef(s) , , , , , . Vous pouvez la mettre en favoris avec ce permalien.
  • http://blog.oxiane.com alain

    Simple mais précis.
    Ça vaut toujours le coup d’être expliqué, même si parfois on a l’impression que tout le monde le sait… hé ben en fait non ! La double écriture (objet, litéral) ne facilite pas l’approche non plus il faut dire.

    • http://www.mathieurobin.com/ Mathieu

      En fait le littéral n’est qu’un raccourci. Si je fais ça :

      monLitteral = { proprieteQuelconque : 2};

      Cela est exactement similaire à ceci :

      monObjet = new Object();
      monObjet.proprieteQuelconque = 2;

      Il est vrai que ça n’aide pas les débutants mais c’est comme pour tout. Si les gens qui forment étaient eux-mêmes au point sur la théorie, on aurait moins de questions à se poser ensuite. Malgré 6 ans d’études, je n’ai jamais étudié ça en cours, je n’ai d’ailleurs étudié que l’approche traditionnelle de la programmation objet. En fait, pour être exact, on ne m’a pas enseigné le JS, je suis passé dans 3 écoles, à chaque fois on m’y a présenté le JS comme une espèce de langage bâtard. Avec toutes les légendes bien répétées comme il faut, bien qu’elles soient toutes infondées. Forcément, en partant de ce genre de formation, on ne peut avoir une bonne approche du Javascript. Du même coup, son modèle objet est vite incompris.

  • adri

    Pour les propriétés et méthodes statiques, que se passe-t-il alors vraiment lorsqu’on défini quelque chose comme ça :

    function constructeurFictif() {};
    constructeurFictif.methodeNonPrototype = function() {};

    et que l’on appelle ensuite ‘methodeNonPrototype’ comme ça :

    var result = constructeurFictif.methodeNonPrototype();

    Merci,

    Adrien

Articles liés