Cours de JavaScript - Les objets dans JavaScript

version 1.2.0, dernière mise à jour le 14 octobre 2024.

   

Table des matières (TdM)

  1. I. Introduction
  1. II. Création et utilisation d'un objet
    1. Création simple d'un objet
    2. Création d'un littéral objet
      1. Exercice : Définitions simples d'objets
  1. III. Plus simple : utiliser une fonction
    1. Introduction
    2. Déclarer un objet comme une fonction
    3. Héritage
      1. Exercice : Création d'objet avec membre privé
    4. Revenons sur this...
      1. Principe
      2. Utilisation de bind
      3. Exercice : Utilisation de bind
  1. IV. Utilisation des classes
    1. Principe
      1. Introduction
      2. Syntaxe
    2. Propriétés et méthodes
      1. Propriétés
      2. Méthodes
    3. Héritage
      1. Exercice : Utilisation de classes

Retour au menu

Contenu du cours

I. Introduction

Le concept de Programmation Orientée Objet ne se limite pas à la définition de classes et d'instances comme dans des langages de programmation comme Java. JavaScript est ainsi un langage de programmation orientée objet, mais il s'agit non pas d'un langage à classes, mais d'un langage à prototypes. Dans ce type de programmation, on ne crée pas tout d'abord une classe abstraite, qu'on instancie ensuite dans des objets « concrets »É; on commence par définir un prototype, qui est directement instancié et peut être manipulé, avant de le « cloner ». Dans le monde réel, pour définir un objet table, on commencerait ainsi par aller dans un atelier de menuiserie et en construire une ; puis on définirait ensuite toutes les autres tables comme étant des variations de ce premier prototype.

Cependant, comme nous le verrons par la suite, un sucre syntaxique imitant les classes a été introduit dans le langage.

II. Création et utilisation d'un objet

1. Création simple d'un objet

Comme JavaScript est orienté prototype, il est possible de créer directement des objets, grâce au mot-clef Object :

var objet1 = new Object ;

objet1.chaine="Bonjour!" ;
objet1.affiche=function(){
  alert(this.chaine) ;
}

objet1.affiche() ;

La déclaration précédente définit l'objet objet1, lui attache une propriété (chaine) et une méthode que l'on appelle ensuite. Ainsi, la méthode est associée à cet objet uniquement, et il n'y a pas de risque que sa définition écrase une autre définition qui serait donnée pour un autre objet.

>Retour à la TdM

2. Création d'un littéral objet

Il est possible d'aller un peu plus loin dans la création d'un objet en utilisant un « littéral objet », même si cette méthode ne fonctionne pas pour les navigateurs très anciens (Netscape jusqu'à la version 3 et Internet Explorer jusqu'à la version 4). La déclaration précédente s'écrit alors…

var objet1 =
{
  chaine:"Bonjour!",
  affiche:function()
    {
      alert(this.chaine) ;
    }
}
objet1.affiche() ;

Cela permet de ne pas avoir à répéter le nom de l'objet défini, réduit donc les risques d'erreur et facilite la maintenance et la reprise ultérieure du code. Attention, dans l'exemple précédent, les lignes dans le littéral sont séparées par des virgules et non des points-virgules, et les définitions utilisent des : et non des signes =.

On peut aussi supprimer une propriété existante à l'aide de l'opérateur delete : par exemple, ici, delete objet1.chaine supprime la propriété d'objet1. Cela ne supprime pas la propriété du prototype, mais uniquement de l'instanciation spécifiée. En mode strict, tenter cette opération sur une propriété non modifiable (par exemple, définie par défaut dans JavaScript comme String.length) lève une exception et déclenche une TypeError.

Exercice 1. Définitions simples d'objets

Énoncé
Correction (définition d'un objet)
Correction (définition d'un littéral objet)

>Retour à la TdM

III. Plus simple : utiliser une fonction

1. Introduction

La manipulation d'un littéral objet peut vite s'avérer fastidieuse et source d'erreurs. Mais Javascript offre une manière assez élégante pour aller assez loin dans la définition d'objets. Cette méthode repose sur les propriétés des fonctions. En Javascript en effet, les fonctions sont des objets de type function. À ce titre, elles possèdent comme tout objet des propriétés et des méthodes (qui sont donc des fonctions appliquées à des fonctions...). Cela ouvre des perspectives intéressantes.

>Retour à la TdM

2. Déclarer un objet comme une fonction

Nous allons utiliser les portées des variables pour créer des propriétés et méthodes privées :

function Personne (qqn)
  {
    let prenom = qqn.prenom? qqn.prenom : "Nino" ;
    this.genre = qqn.genre? qqn.genre : "homme" ;
    this.dateNaissance = qqn.dateNaissance? qqn.dateNaissance : 1934 ;
    this.caractere = qqn.caractere? qqn.caractere : "musicien" ;

    this.getPrenom = function()
      {
        return prenom ;
      } ;

    this.setPrenom = function(x)
      {
         prenom=x ;
      } ;

    function vieillit()
      {
        dateNaissance-- ;
      }
  }

var anatole = new Personne ({prenom: "Anatole", "genre: homme", dateNaissance:1990, caractere: "frivole"}) ;
alert(anatole.genre);//renvoie "homme"

var bernadette = new Personne ({prenom: "Bernadette", genre: "femme", dateNaissance: 1991, caractere: "très chouette"}) ;
alert(bernadette.genre);//renvoie "femme"

alert(anatole.prenom);//renvoie undefined car prenom est une propriété privée

alert(anatole.getPrenom());//renvoie Anatole

bernadette.setPrenom("Noémie") ;
alert(bernadette.getPrenom());//renvoie Noémie

bernadette.vieillit();//renvoie une erreur car vieillit n'est pas une méthode publique

Tout ce qui commence par this est défini comme une propriété ou une méthode publique de la fonction, donc esst appelable de l'extérieur. Le reste est privé : on peut l'utiliser (comme la fonction vieillit par exemple) dans la fonction, mais pas à l'extérieur.

>Retour à la TdM

3. Héritage

Nous avons déjà évoqué la propriété prototype . Elle s'applique tout aussi facilement à nos objets nouvellement définis, et cela permet de construire un mécanisme en tout point similaire à l'héritage en programmation orientée objet « classique ». Supposons notre prototype Personne défini comme précédemment. On peut définir un nouveau prototype, qui pourrait être etudiant, sous la forme :

function Etudiant(fiche, note=0)
    {
      Personne.call(fiche, note) ;

      let etudiantNote = note ;

      this.getNote = function()
        {
          return etudiantNote ;
        } ;
    }

Etudiant.prototype = Object.create(Personne.prototype) ;
Etudiant.prototype.constructor = Etudiant ;

On peut alors écrire par exemple...

var marieBerthe = new Etudiant ( {prenom:"Marie-Berthe", genre:"femme", caractere:"experte"},17) ;
alert(marieBerthe.getNote());//renvoie 17

Exercice 1. Création d'objet avec membre privé

Énoncé
Correction (Constructeur sans valeurs par défaut)
Correction (Constructeur avec valeurs par défaut)

>Retour à la TdM

4. Revenons sur this...

a. Principe

Le mot-clef this, que nous avons déjà eu l'occasion de rencontrer, a un comportement en apparence déroutant. Il fait en effet référence au contexte dans lequel une fonction est appelée. Considérons l'exemple suivant...

var fratrie =   {
  prenom1: 'Abel' ,
  prenom2: 'Yves' ,
  prenom3: 'Hakim' ,
  nom: 'Flaille' ,
  toutLeMonde: function()    {
    console.log(this.prenom1 + ', ' + this.prenom2 + ', ' + this.prenom3 + ', ' + this.nom) ;
    }
  } ;
fratrie.toutLeMonde() ;
var tlm=fratrie.toutLeMonde() ;
tlm() ;

Le premier exemple renvoie "Abel, Yves, Hakim Flaille", le second undefined. Dans le premier cas en effet, la fonction toutLeMonde est appelée en tant que méthode de l'objet fratrie  son contexte est donc celui de l'objet appelant, fratrie et par exemple this.prenom1 renvoie à fratrie.prenom1. Dans le second cas, la fonction tlm est appelée au niveau global, donc le contexte est celui de l'objet global, à savoir window. this.prenom1 renvoie donc à window.prenom1, qui n'est pas défini...

b. Utilisation de bind

Un moyen existe pour spécifier l'objet auquel this doit référer : la méthode bind de la propriété Function.prototype, qui est héritée par toute fonction. Il suffit d'écrire ainsi var tlm=fratrie.toutLeMonde.bind(fratrie); pour que tlm() renvoie Abel, Yves, Hakim Flaille". On peut aussi expliciter le contexte lors de la définition de la méthode :

var fratrie =   {
  prenom1: 'Abel' ,
  prenom2: 'Yves' ,
  prenom3: 'Hakim' ,
  nom: 'Flaille' ,
  toutLeMonde: function()    {
    return this.prenom1 + ', ' + this.prenom2 + ', ' + this.prenom3 + ', ' + this.nom ;
    }
  } ;

Cette méthode permet de gérer le contexte dans lequel un gestionnaire d'événement est appelé, ou, plus généralement, un callback, c'est-à-dire une fonction passée en paramètre d'une autre fonction. Par exemple...

var fratrie =   {
  prenom1: 'Abel' ,
  prenom2: 'Yves' ,
  prenom3: 'Hakim' ,
  nom: 'Flaille' ,
  toutLeMonde: function()    {
    document.getElementById("ident").addEventListener("click",function()      {
      console.log(this.nom) ;
      }.bind(this), false) ;
    }
  } ;

Exercice 1. Utilisation de bind

Énoncé
Correction (Création de la structure de base)
Correction (Explicitation de this)

>Retour à la TdM

IV. Utilisation des classes

1. Principe

a. Introduction

EcmaScript 2015 a introduit une syntaxe de classes dans JavaScript. Cependant, cette syntaxe n'est qu'un vernis sur ce qui reste le fondement de JavaScript, à savoir les prototypes. Partant, un certain nombre de concepts ne sont pas utilisables, et d'autres particularités dérivent du modèle sous-jacent de JavaScript. Par la suite, nous parlerons de classe mais il faut bien garder à l'esprit qu'il ne s'agit pas de classes au même titre qu'en Java, par exemple, mais juste une commodité de notation et de déclaration.

Notamment, toute la définition d'une classe est en mode strict.

b. Syntaxe

Une classe est définie à l'aide du mot-clef class. Contrairement à la définition de fonctions, une classe doit être définie avant d'être instanciée (contrairement aux définitions de fonction en JavaScript, dont les déclarations peuvent « remonter » —c'est ce qu'on appelle le hoisting. On écrira ainsi…

class MaClasse {
  constructor(…)  {
    this.prop1=… ;
  }
  methode1()  {
    //Code de la méthode
  }
}
let monObjet = new maClasse(…) ;

constructor est un nom réservé. Par exemple…

class Voiture {
  constructor(modele, nombreRoues)  {
    this.marque=modele ;
    this.nombreRoues=nombreRoues ;
  }
  demarre()  {
    alert ("Vroum!") ;
  }
}
let maPetiteVoiture = new Voiture("Avtoros Shaman", 8) ;

Attention, il n'y a pas de virgule ou de point virgule entre les différents éléments constitutifs de la déclaration. Par exemple, il n'y a pas de virgule entre le constructeur et la définition de la méthode, car il ne s'agit pas d'un littéral objet.

>Retour à la TdM

2. Propriétés et méthodes

a. Propriétés

On ajoute simplement des propriétés, publiques ou privées :

class Voiture {
  constructor(modele, nombreRoues)  {
    this.marque=modele ;
    this.nombreRoues=nombreRoues ;
  }

  attacheCaravane//propriété publique silmplement déclarée (vaut undefined)
  autoRadio = false;//propriété publique avec valeur par défaut

  #codeAlarme = 1234;//propriété privée

  demarre()  {
    alert ("Vroum!") ;
  }
}
let maPetiteVoiture = new Voiture("Avtoros Shaman", 8) ;

b. Méthodes

Une méthode publique se définit simplement comme on l'a vu plus haut. Il est cependant possible de définir des méthodes dites « statiques ». Ces méthodes sont propres à la classe et peuvent être utilisées au sein de celle-ci, mais ne peuvent pas être appliquées à une instance en particulier. Par exemple…

class Tab {
	constructor(tableau){
			this.tableau=tableau;
		}
					
		getLongueur () {
			return this.tableau.length;	
		}
						
		item(i) {
			return this.tableau[i];	
		}
						
		static tabSoustrait(a, b){
			var res=new Array;
		
			for (let i=0;i<a.getLongueur();res.push(a.item(i)-b.item(i++)));
			
			return res;
		}
}
							
var x = new Tab([5,6,7]);
var y = new Tab([3,3,12]);
							
console.log(Tab.tabSoustrait(x,y));

En passant, vous pouvez d'ailleurs remarquer dans l'exemple précédent qu'on ne peut pas écrire directement a.length ou a[i] dans la méthode statique, puisque a et b ne sont pas de « vrais » tableaux. On doit recourir à des définitions de méthodes.

>Retour à la TdM

3. Héritage

On peut créer une sous-classe d'une classe existante avec l'instruction extends. Cette instruction permet d'étendre notamment la définition des méthodes de la classe parente, voire de créer de nouvelles méthodes. Par exemple…

class SportCollectif {
	constructor(nom, nbjoueurs){
		this.nom=nom;
		this.nbjoueurs=nbjoueurs;		
	}

	get_joueurs(){

		return this.nbjoueurs;
		}
}

class Sport15 extends SportCollectif {
	constructor(nom) {
		super(nom, 15);	
	}

	get_joueurs(){
		var nomJoueurs="";
		if (this.nom=="rugby") nomJoueurs+=" rugbymen";
		return this.nbjoueurs+nomJoueurs;
	}
}

var sport1 = new SportCollectif("football", 11);
var sport2 = new Sport15("sport");
var sport3 = new Sport15("rugby");

console.log(sport1.get_joueurs()); // 11
console.log(sport2.get_joueurs()); // 15
console.log(sport3.get_joueurs()); // 15 rugbymen

Exercice 1. Utilisation de classes

Énoncé
Correction

>Retour à la TdM

Historique de ce document

Conditions d'utilisation et licence

Creative Commons License
Cette création est mise à disposition par Gilles Chagnon sous un contrat Creative Commons.

Retour au menu