Cours de JavaScript - Les objets dans JavaScript

version 1.1.1, dernière mise à jour le 11 décembre 2018.

   

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. Interdire la manipulation de certaines propriétés : l'encapsulation
    4. Changer l'ordre des paramètres et proposer des valeurs par défaut
    5. ... et pour les méthodes privées ?
    6. Héritage
      1. Exercice : Création d'objet avec membre privé
    7. 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. Création d'une sous-classe
      1. Exercice : Utilisation de classes

Retour au menu

Contenu du cours

I. Introduction

Il arrive que des variables soient redéfinies entre plusieurs scripts épars dans des fichiers distincts. Une redéfinition malencontreuse d'une variable peut perturber le fonctionnement d'un script, et il faut donc au maximum limiter le risque d'interférences entre deux variables. La toute première précaution à prendre est donc de limiter au maximum la portée d'une variable, en recourant à la déclaration var.

On pourrait penser à utiliser des noms de variables très explicites, voire à les préfixer ou les suffixer, comme par exemple monCodeAMoiRienQuAMoi_maVariable, mais il faut pour cela trouver un préfixe unique (celui de l'exemple précédent a certes peu de chances d'être repris !), et surtout, par la suite, se garder des fautes de frappe. Qui plus est, cela a comme inconvénient que si par extraordinaire, le préfixe est utilisé dans un autre fichier, l'ensemble des variables et fonctions doit être renommé. Ce n'est donc pas une solution très efficace.

II. Création et utilisation d'un objet

1. Création simple d'un objet

JavaScript ne permet pas encore comme d'autres langages de définir des classes, et d'instancier ensuite des objets. Mais 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 », mais 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

Commençons par créer une fonction de base, qui va nous servir de « prototype » (nous verrons par la suite que ce terme n'est pas utilisé sans raison) :

function Personne (prenom, genre, dateNaissance, caractere)
  {
    this.prenom=prenom ;
    this.genre=genre ;
    this.dateNaissance=dateNaissance ;
    this.caractere=caractere ;
  }

var anatole = new Personne ("Anatole", "homme", 1990, "frivole") ;
alert(anatole.genre);//renvoie "homme"

var bernadette = new Personne ("Bernadette", "femme", 1991, "très chouette") ;
alert(bernadette.genre);//renvoie "femme"

Cette solution, pour être simple, n'en présente pas moins deux inconvénients :

>Retour à la TdM

3. Interdire la manipulation de certaines propriétés : l'encapsulation

Il est possible d'aller encore un peu plus loin. En effet, il n'est parfois pas souhaitable que certaines propriétés d'un objet soient accessibles de l'« extérieur » du script. En programmation orientée objet, on fait appel, dans une classe, à des membres dits privés et d'autres publics. Il est possible de faire de même en javascript, en utilisant le fait que la portée des variables déclarées dans une fonction y est limitée. Ainsi,

function Personne (prenom, genre, dateNaissance, caractere)
  {
    var personnePrenom=prenom ;
    var personneGenre=genre ;
    var personneDateNaissance=dateNaissance ;
    var personneCaractere=caractere ;

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

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

var anatole = new Personne ("Anatole", "homme", 1990, "frivole") ;
alert(anatole.personnePrenom);//renvoie undefined car personnePrenom est une propriété privée

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

var bernadette = new Personne ("Bernadette", "femme", 1991, "très chouette") ;
bernadette.setPrenom("Noémie") ;
alert(bernadette.getPrenom());//renvoie Noémie

>Retour à la TdM

4. Changer l'ordre des paramètres et proposer des valeurs par défaut

Afin de ne pas être contraint dans l'appel de la fonction dans l'ordre des arguments, on peut lui fournir un objet, et en profiter pour proposer des valeurs par défaut si certaines propriétés sont manquantes (la méthode ES6 ne permet pas de modifier l'ordre de déclaration des paramètres) :

function Personne (fiche)
  {
    var personnePrenom = typeof fiche.prenom !== 'undefined' ? fiche.prenom : "Nino" ;
    var personneGenre = typeof fiche.genre !== 'undefined' ? fiche.genre : "homme" ;
    var personneDateNaissance = typeof fiche.dateNaissance !== 'undefined' ? fiche.dateNaissance : "1934" ;
    var personneCaractere = typeof fiche.caractere !== 'undefined' ? fiche.caractere : "musicien" ;

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

    this.getDateNaissance = function()
      {
        return personneDateNaissance ;
      } ;

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

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

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

>Retour à la TdM

5. ... et pour les méthodes privées ?

Il suffit de déclarer la méthode privée en tant que fonction, à l'intérieur de la fonction de base. Par exemple :

function Personne (fiche)
  {
    var personnePrenom = typeof fiche.prenom !== 'undefined' ? fiche.prenom : "Nino" ;
    var personneGenre = typeof fiche.genre !== 'undefined' ? fiche.genre : "homme" ;
    var personneDateNaissance = typeof fiche.dateNaissance !== 'undefined' ? fiche.dateNaissance : "1934" ;
    var personneCaractere = typeof fiche.caractere !== 'undefined' ? fiche.caractere : "musicien" ;

    function vieillit()
      {
        personneDateNaissance-- ;
      }

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

    this.getDateNaissance = function()
      {
        return personneDateNaissance ;
      } ;

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

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

... renvoie un message d'erreur car vieillit, appliquée à bernadette, est undefined. Cependant, cela n'empêche pas d'utiliser vieillit à l'intérieur de Personne.

>Retour à la TdM

6. 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)
    {
      Personne.call(fiche, note) ;

      var etudiantNote = typeof note !== 'undefined' ? note : 0 ;

      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

7. 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

Il n'est actuellement pas possible de définir de propriété, ni publique, ni privée. La mise au point du mécanisme, encore expérimental, dépend encore du navigateur utilisé et on ne peut pas encore l'utiliser en production.

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. Création d'une sous-classe

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