Prototype VS Class – JS n’est pas un langage orienté objet

Partager surShare on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Email this to someonePrint this page

En 2016 JavaScript est partout. On le retrouve aussi bien côté client (front-end) que côté serveur (back-end). Son fort engouement à tracté une nouvelle génération de développeurs ayant rapidement adopté ses us et coutumes. Mais lorsque l’on pose la question fondamentale suivante à la plupart de ces développeurs, à savoir « En quoi JavaScript et ses prototypes différent des classes de POO ».

Beaucoup (trop) avouent ne jamais s’être posé la question voir ne pas connaître la réponse.

L’objectif de cet article est donc de clarifier et d’expliquer le modèle prototypal propre à JS et ses différences avec un système de classes d’un langage type PHP.


Php Logo

Langages de Classes (orienté objet) : PHP

Qu’est-ce qu’un langage orienté objet ?

3 notions simple à connaître et propres aux langages orientés class:

  1. Notion de Classe
  2. Notion d’Instance de Classe
  3. Notion de Héritage et Polymorphisme

Classe

C’est le squelette d’un Objet en programmation orienté objet. Une Classe définit les méthodes et attributs propres d’un objet. Pour rappel, une classe contient des variables (privées ou non), des méthodes, ainsi qu’un constructeur.

« Une classe est un schéma — c’est une description d’un objet qui va être créé. »

— Eric Elliot

Instance de Classe

Une instance de classe est tout simplement un objet avec un comportement et un état, tous deux définis par la classe. Il s’agit donc d’un objet constituant un exemplaire de la classe.

L’instanciation d’une classe se fait avec le mot-clé new.

par exemple:

$comptable = new Employe();

Ici Comptable est une instance de la classe Employe.

Héritage et Polymorphisme

Héritage

Héritage example

Exemple basique de diagramme UML représentant une relation d’héritage — source: Wikipédia

L’héritage est un principe propre à la POO (Programmation Orientée Objet). Il permet de créer une nouvelle classe à partir d’une classe existante.

Un principe d’héritage induit deux choses:

  • Une Classe « mère » (dans l’exemple ci-dessus, la classe "Animal")
  • Une ou plusieurs Classe(s) « fille » (dans l’exemple ci-dessus, les classes "Chat" et "Chien")

Toute classe « fille » contient les attributs et les méthodes de sa superclasse (la classe « mère »). L’intérêt majeur de l’héritage est de pouvoir définir de nouveaux attributs et de nouvelles méthodes pour la classe « fille », qui viennent s’ajouter à ceux et celles héritées de la classe « mère ».

Polymorphisme

 

Le polymorphisme (du grec: qui peut prendre plusieurs formes) est le concept qui entre en compte lorsque l’on redéfini une méthode de classe mère dans une classe fille. Alors que l’héritage concerne les classes (et leur hiérarchie), le polymorphisme est relatif aux méthodes des objets.

Prenons l’exemple de notre Classe mère « Animal » et de nos deux classes filles « Chat » et « Chien ».

Un exemple de polymorphisme serait le suivant:

  • Méthode « parler() » dans la classe « Animal »
  • Redéfinition de la méthode « parler() » dans la classe « Chien » où le retour est « Wouf Wouf! »
  • Redéfinition de la méthode « parler() » dans la classe « Chat » où le retour est « Miaou ! »

Qu’importe la classe appelée par la suite, et le nombre de classe fille, il suffira d’indiquer au programme, dans une boucle par exemple: Animal.parler() et comme nous avons redéfini la méthode « parler() » chaque animal aura un comportement différent (« Wouf wouf! » pour une instance « Chien », « Miaou ! » pour une instance « Chat »).

 

JavaScript Logo

Langages de Prototypes : JavaScript

Pour les langages prototypals, nous allons nous rendre compte qu’il y a certaines similitudes avec les langages orienté classes, mais qu’il existe des différences remarquable dans leur concept clés et fonctionnement.

Pour commencer, voici 3 notions simples à connaître et propres aux langages prototypals:

 

  1. Notion d’Objet
  2. Notion de Prototype
  3. Notion de Héritage et Polymorphisme

 

Objet

Un objet est un ensemble de propriétés et une propriété est une association entre un nom (aussi appelé clé) et une valeur.

var monObjetToto = {
 prenom: 'Toto',
 nom: 'Doe',
 age: 24,
 sexe: 'Homme',
 sayHello: function(){
  return 'Hello, my name is ' + this.prenom; // this fait référence à monObjetToto
 }
};



Ici « monObjetToto » est un objet javascript possèdant 4 propriétés (« prenom », « nom », « age », « sexe ») et une méthode (fonction sayHello() ).

On comprend alors la fonction et l’utilité principale des objets JavaScript de « conteneurs de propriétés« .

Prototype

Qu’est-ce qu’un prototype ?

 

Tout objet a un attribut « prototype ». Mais qu’est-ce qu’un Prototype ?

— C’est en fait un pointeur vers un autre objet parent

 

« Un prototype est une instance d’un objet. Un objet hérite directement d’un voir plusieurs autres objets. »

— Eric Elliot

Si l’on essaye d’accéder à une propriété de l’objet et que celui n’existe pas, JavaScript va de lui même aller chercher dans le prototype de cet objet (soit dans l’objet parent) voir si cette propriété existe.

JavaScript va alors remonter toute la « chaîne de prototypes » qui correspond aux différents niveaux d’héritage de l’objet, jusqu’à tomber sur une valeur null.
Dans ce cas, JavaScript va retourner la fameuse bête noire de tout développeur JS:

undefined

Voici un exemple pratique afin de cerner ce principe de chaîne de prototypes:

// On a l'objet o, qui a des propriétés propres a et b:
// {a: 1, b: 2}
// o.[[Prototype]] a les propriétés b et c:
// {b: 3, c: 4}
// Enfin, o.[[Prototype]].[[Prototype]] vaut null. C'est 
// donc la fin de la chaîne de prototype. Par définition,
// null n'a pas de [[Prototype]].
// La chaîne de prototypes ressemble donc à :
// {a:1, b:2} ---> {b:3, c:4} ---> null

console.log(o.a); // 1
// Est-ce que 'a' est une propriété propre de o ? Oui, elle vaut 1.

console.log(o.b); // 2
// Est-ce que 'b' est une propriété propre de o ? Oui, elle vaut 2.
// Le prototype possède aussi une propriété 'b' mais celle-ci n'est pas
// utilisée.
// On parle alors de « masque de propriété » (property shadowing)

console.log(o.c); // 4
// Est-ce que 'c' est une propriété propre de o ? Non, on vérifie le prototype
// Est-ce que 'c' est une propriété propre de o.[[Prototype]] ?
// Oui, elle vaut 4.

console.log(o.d); // undefined
// Est-ce que 'd' est une propriété propre de o ? Non, on vérifie le prototype
// Est-ce que 'd' est une propriété propre de o.[[Prototype]] ?
// Non on vérifie son prototype.
// o.[[Prototype]].[[Prototype]] vaut null, on arrête la recherche.
// La propriété n'a pas été trouvée, on renvoie undefined

Héritage et Polymorphisme

L’héritage au sein de JavaScript est basé non pas sur des classes (comme dans les langage orienté objet type PHP) mais sur les prototypes.

Le prototype est donc LA brique fondamentale du langage JavaScript.

Dès que l’on parle d’héritage, JavaScript ne va alors utiliser qu’un seul concept: les objets.

Ce modèle d’héritage propre à JavaScript est souvent décrié et considéré comme un point faible car mal compris. Or il se révèle être plus puissant que le modèle classique d’héritage des langages orientés objet type PHP basés sur un système de Classes.

Pourquoi ?

— Car on peut par exemple construire assez facilement un modèle classique à partir d’un modèle basé sur des prototypes.

— L’accès à la « chaîne des prototype » est aussi un outil très puissant avec la possibilité de remonter la chaîne des prototypes d’un objet (voir plus haut, paragraphe introductif sur les Prototypes) afin de trouver une propriété.

 

Mais alors, comment applique t-on ce principe d’héritage ?

Avec un constructeur

Ça tombe bien, car chez JavaScript, toute fonction peut être un constructeur une fois qu’on l’appelle avec l’opérateur new !

 

function Personne(nom, prenom, age, sexe){
 this.nom = nom;
 this.prenom = prenom;
 this.age = age;
 this.sexe = sexe;
 this.sayHello = function() {
  return 'Hello ! My name is ' + this.prenom
 }
};

var toto = new Personne("Thomas", "Bro", 24, "Homme");

console.log( toto.nom ); // "Bro"
console.log( toto.prenom ); // "Thomas"
console.log( toto.age ); // 24
console.log( toto.sexe ); // "Homme"
console.log( toto.sayHello() ); // "Hello ! My name is Thomas"

Ici, nous venons de créer un objet « toto » qui hérite de la function « Personne » qui est en fait un constructeur.

Avec Object.create()

var personne = {
 nom: '',
 prenom: '',
 age: null,
 sexe: '',
 sayHello: function(){
  return 'Hello ! My name is ' + this.prenom;
 }
};

var toto = Object.create(personne);
console.log( toto.nom ); // "Bro"
console.log( toto.prenom ); // "Thomas"
console.log( toto.age ); // 24
console.log( toto.sexe ); // "Homme"
console.log( toto.sayHello() ); // "Hello ! My name is Thomas"

Avec le mot-clé class

"use strict"

class Personne {
 constructor(nom, prenom, age, sexe){
  this.nom = nom;
  this.prenom = prenom;
  this.age = age;
  this.sexe = sexe;
 }
 sayHello() {
  return 'Hello ! My name is ' + this.prenom;
 }
};

class Homme extends Personne {
 constructor(nom, prenom, age, couleurFav, taille){
   super(nom, prenom, age, "Homme");
   this.couleurFav = couleurFav;
   this.taille = taille;
 }
 get myFavoriteColour(){
  return this.couleurFav;
 }
}


var toto = new Homme("Doe", "Thomas", 24, "bleu", 175);
console.log( toto.nom ); // "Bro"
console.log( toto.prenom ); // "Thomas"
console.log( toto.age ); // 24
console.log( toto.sexe ); // "Homme"
console.log( toto.sayHello() ); // "Hello ! My name is Thomas"

console.log( toto.myFavoriteColour ); // "Bleu"

 

ES6 et l’introduction du mot-clé "Class"

 

Avec l’arrivée de ES6, nous avons pu remarquer l’apparition du mot clé Class, qui à mon sens est une aberration ainsi qu’un abus de langage.

Pourquoi ?

— Tout simplement parce que, de par sa nature, une « Class » JavaScript n’est pas la même chose qu’une « Class » PHP (type langage orienté objet). Ce mot-clé fait référence à un objet Function qui sert de constructeur et possède un objet prototype Empty lié, qui correspond au fameux this.

Le mot-clé class fait donc en fait référence à Object.prototype en JS.

 

JavaScript possède un typage dynamique et ne fournit pas d’implémentation de classe (le mot-clé class a été introduit avec ECMAScript 6 mais ne fournit qu’un sucre syntaxique, JavaScript continue d’avoir un modèle d’héritage basé sur les prototypes).

Source – Mozilla.com

 

Résumé des différences

Langage de classe (PHP) Langage de prototype (JavaScript)
Les classes et les instances sont deux entités distinctes. Tous les objets sont des instances.
Une classe est définie avec une définition de classe. On instancie une classe avec des méthodes appelées constructeurs On définit et on crée un ensemble d’objets avec des fonctions qui sont des constructeurs.
On crée un seul objet grâce à l’opérateur new. Même chose que pour les langages de classe.
On construit une hiérarchie d’objets en utilisant les définitions des classes pour définir des classes-filles à partir de classes existantes. On construit une hiérarchie d’objets en assignant un prototype à un objet dans le constructeur de cet objet.
Les objets héritent des propriétés appartenant à la chaîne des classes de la hiérarchie. Les objets héritent des propriétés appartenant à la chaîne des prototypes de la hiérarchie.
La définition de la classe définit exactement toutes les propriétés de toutes les instances d’une classe.Il est impossible d’ajouter des propriétés dynamiquement pendant l’exécution. Le constructeur ou le prototype définit un ensemble de propriétés initiales. Il est possible d’ajouter ou de retirer des propriétés dynamiquement, pour certains objets en particuliers ou bien pour l’ensemble des objets.

source: Mozilla.org

 

Voilà ! J’espère que cet article vous aura plus et vous permettra d’y voir un peu plus clair et de mieux comprendre JS ainsi que la subtile différence entre classes et prototypes.

 


Ressources:

Partager surShare on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Email this to someonePrint this page

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *