Utiliser les objets

JavaScript est basé sur un paradigme objet. Un objet est un ensemble de propriétés et une propriété associe un nom à une valeur. La valeur d'une propriété peut être une fonction : elle sera alors appelée méthode de l'objet. Il existe des objets prédéfinis au sein du navigateur mais il est également possible de définir ses propres objets.

Ce chapitre permet de voir comment utiliser des objets, des propriétés, des fonctions, des méthodes et comment créer ses propres objets.

Un aperçu des objets

En JavaScript, comme avec de nombreux autres langages de programmation, les objets peuvent être comparés aux objets de la vie de tous les jours. Il est possible de comprendre le concept d'objet JavaScript en utilisant des objets réels.

En JavaScript, un objet est une entité indépendante, portant des propriétés et un type. Si, par exemple, on considère une tasse : une tasse est un objet, qui possède des propriétés comme sa couleur, sa forme, son matériau... De façon similaire, un objet JavaScript disposera de propriétés définissant ses caractéristiques.

Les objets et les propriétés

Un objet javaScript possède des propriétés qui lui sont associées. Une propriété peut êtrer vue comme une variable associée à l'objet. Les propriétés d'un objet sont des variables classiques à l'exception du lien qui les rattache à l'objet. Les propriétés d'un objet permettent de définir les caractéristiques d'un objet et il est possible d'accéder aux propriétés d'un objet en utilisant le nom de l'objet, un point et le nom de la propriété.

nomDeMonObjet.nomDeLaPropriete

Comme toutes les variables en JavaScript, le nom de l'objet et le nom d'une propriété sont sensibles à la casse. Il est possible de définir une propriété en lui assignant une valeur. On peut donc créer un objet appelé maVoiture et lui donner les propriétés fabricant, modèle, et année de la façon suivante :

var maVoiture = new Object();
maVoiture.fabricant = "Ford";
maVoiture.modèle = "Mustang";
maVoiture.année = 1969;

On peut également accéder aux propriétés des objets JavaScript en utilisant des crochets. Les objets sont parfois appelés tableaux associatifs, car chaque propriété est associée via un nom qui est une chaîne de caractère qu'on peut utiliser pour y accéder. Par exemple, on pourrait accéder aux propriétés de maVoiture :

myCar["fabricant"] = "Ford";
myCar["modèle"] = "Mustang";
myCar["année"] = 1969;

Le nom d'une propriété d'un objet peut être n'importe quelle chaîne de caractères JavaScript conforme, y compris la chaîne vide. En revanche, toute propriété dont le nom n'est pas un identifiant JavaScript valide (qui comporte un espace, un tiret ou qui commence par un chiffre) pourra n'être consultée qu'avec cette notation utilisant les crochets. Cette notation peut se révéler utile lorsque les noms des propriétés sont chargés dynamiquement (on ne connaît le nom de la propriété qu'au moment de l'exécution). Voici quelques exemples :

var monObjet = new Object(),
    str = "maChaine",
    rand = Math.random(),
    obj = new Object();

monObjet.type              = "Dot syntax";
monObjet["date created"]   = "String with space";
monObjet[str]              = "String value";
monObjet[rand]             = "Random Number";
monObjet[obj]              = "Object";
monObjet[""]               = "Even an empty string";

console.log(monObjet);

On peut également accéder aux propriétés d'un objet en utilisant une chaîne de caractères enregistrée dans une variable :

var nomPropriété = "fabricant";
myCar[nomPropriété] = "Ford";

nomPropriété = "modèle";
myCar[nomPropriété] = "Mustang";

Les crochets peuvent être utilisés avec for...in pour effectuer une boucle sur les propriétés d'un objet qu'on peut énumérer. Voici, par exemple, une fonction qui affiche les propriétés d'un objet lorsqu'on passe l'objet et le nom de l'objet comme arguments de la fonction :

function afficheProp(obj, nomObj) {
  var result = "";
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
        result += nomObj + "." + i + " = " + obj[i] + "\n";
    }
  }
  return result;
}

Ainsi, l'appel de la fonction afficheProp(maVoiture, "maVoiture") renverrait :

maVoiture.fabricant = Ford
maVoiture.modèle = Mustang
maVoiture.année = 1969

Tout est objet (presque)

Quasiment tout, en JavaScript, est un objet. Tous les types primitifs, exceptés null et undefined sont traités comme des objets. On peut leur assigner des propriétés (attention pour certains types, les propriétés assignées ne seront pas persistentes) et ils possèdent l'ensemble des caractéristiques d'un objet.

Lister toutes les propriétés d'un objet

Depuis ECMAScript 5, il existe trois façons, natives, de lister les propriétés d'un objet :

  • for...in
    De cette façon, on parcourt toutes les propriétés énumérables d'un objet et de sa chaîne de prototype
  • Object.keys(o)
    Avec cette méthode, on renvoie un tableau des noms toutes les propriétés énumérables propres à un objet (ce qui exclue celles de la chaîne de prototype), ces propriétés sont appelées les clés de l'objet o.
  • Object.getOwnPropertyNames(o)
    Cette méthode renvoie un tableau contenant les propriétés propres de l'objet o (énumérables ou non).

En ECMAScript 5, il n'existe aucun moyen natif de lister toutes les propriétés d'un objet mais cela peut être fait avec la fonction suivante :

function listAllProperties(o){     
	var objectToInspect;     
	var result = [];
	
	for(objectToInspect = o; objectToInspect !== null; objectToInspect = Object.getPrototypeOf(objectToInspect)){  
		result = result.concat(Object.getOwnPropertyNames(objectToInspect));  
	}
	
	return result; 
}

Cette fonction peut être utile pour révéler certaines propriétés qui auraient pu être cachées (par exemple des propriétés dans la chaîne de prototype qui ne sont pas accessibles car une autre propriété possède le même nom, plus tôt dans la chaîne de prototype). On peut facilement réduire cette liste aux propriétés accessibles d'un objet en retirant les duplicats du tableau.

Créer de nouveaux objets

JavaScript possède un certain nombre d'objets prédéfinis auxquels il est possible d'ajouter ses propres objets. Depuis JavaScript 1.2, il est possible de créer un objet en utilisant un initialisateur d'objet. Il est également possible de créer une fonction qui est un constructeur et ensuite d'appeler cette fonction avec l'opérateur new.

Utiliser les initialisateurs d'objet

On peut créer des objets en utilisant un constructeur (qui est une fonction), on peut également créer des objets en utilisant un initialisateur. Les initialisateurs d'objets correspondent à la façon littérale de créer un objet. Le terme d'initialisateur peut être trouvé, avec la même signification, en C++.

Voici la syntaxe correspondant à l'utilisation d'un initialisateur d'objet :

var obj = { propriété_1:   valeur_1,   // propriété_# peut être un identifiant...
            2:             valeur_2,   // ou un nombre
            // ...,
            "propriété n": valeur_n }; // ou un nombre

Ici, obj est le nom du nouvel objet qu'on souhaite créer, propriété_i est un identifiant (soit un nom, un nombre ou une chaîne de caractères) et chaque valeur_i est une expression dont la valeur est assignée à la propriété_i. L'assignation à obj est facultatif, si on n'a pas besoin de faire référence à l'objet par la suite, il n'y a pas besoin de l'assigner à une variable. (Il peut être conseillé d'entourer ce littéral par des parenthèses si celui-ci apparaît dans une instruction, ce afin de ne pas le confondre avec un bloc d'instructions.)

Si un objet est créé avec un initialisateur au plus haut niveau, JavaScript interprètera l'objet chaque fois qu'il évaluera une expression qui contient le littéral objet. Aussi, un initialisateur qui est utilisé dans une fonction est créé chaque fois que la fonction est appelée.

L'instruction qui suit crée un objet et l'assigne à la variable x si et seulement si l'expression cond est vérifiée :

if (cond) var x = {cave: "Johnson"};

L'exemple suivant permet de créer un objet maTwingo qui a trois propriétés. La propriété moteur est également un objet avec ses propres propriétés.

var maTwingo = {couleur: "vertes", nbRoues: 4, moteur: {nbCylindre: 4, vol: 2.2}};

On peut également utiliser des initialisateurs pour créer des tableaux. Voir les littéraux de tableaux.

Avec JavaScript 1.1 et les versions antérieures, il était impossible d'utiliser de tels initialisateurs et on ne pouvait utiliser que les constructeurs ou une fonction d'un autre objet : c'est ce qui est décrit dans le paragraphe suivant.

Utiliser un constructeur

On peut également créer des objets en effectuant les étapes suivantes :

  1. On définit le type de l'objet en écrivant une fonction qui est un constructeur. Dans ce cas, on respecte la convention qui est d'utiliser une majuscule comme première lettre (il y a de bonnes raisons pour ça).
  2. On crée une instance de l'objet grâce au mot-clé new.

Afin de définir un type d'objet, on doit créer une fonction pour le type d'objet qui définit son nom, ses propriétés et ses méthodes. Si on souhaite créer un type d'objet pour les voitures, on voudra que le nom de ce type soit voiture, et on pourrait souhaiter avoir des propriétés comme la marque, le modèle et l'année. Pour ce faire, on écrit la fonction suivante :

function Voiture(fabricant, modèle, année) {
  this.fabricant = fabricant;
  this.modèle = modèle;
  this.année = année;
}

On voit ici l'utilisation du mot-clé this afin d'assigner les valeurs, passées en arguments de la fonction, aux propriétés de l'objet.

On peut désormais créer un objet maVoiture de la façon suivante :

var maVoiture = new Voiture("Renault", "Twingo", 2006);

Cette instruction crée maVoiture et assigne les valeurs données à ses propriétés. A présent, la valeur de maVoiture.fabricant est la chaîne de caractères "Renault", maVoiture.année est l'entier 2006...

On peut créer autant d'objets voitures en appelant à chaque fois new. Ainsi :

var voitureA = new Voiture("Nissan", "300ZX", 1992);
var voitureB = new Voiture("Mazda", "Miata", 1990);

Un objet peut avoir des propriétés qui sont elles-mêmes des objets. Si, par exemple, on définit un objet personne :

function Personne(nom, age, sexe) {
  this.nom = nom;
  this.age = age;
  this.sexe = sexe;
}

et qu'on instancie deux objets personne :

var amelia = new Person("Amelia Williams", 33, "F");
var cave = new Person("Cave Johnson", 42, "H");

On peut ensuite réécrire la définition de voiture pour ajouter une propriété propriétaire qui sera un objet personne :

function Voiture(fabricant, modèle, année, propriétaire) {
  this.fabricant = fabricant;
  this.modèle = modèle;
  this.année = année;
  this.propriétaire = propriétaire;
}

To instantiate the new objects, you then use the following:

var voitureA = new Car("Nissan", "300ZX", 1992, amelia);
var voitureB = new Car("Mazda", "Miata", 1990, cave);

On voit ici que ce n'est pas une chaîne de caractères ou un entier qui est passé en argument mais bien les objets amelia et cave qui seront les propriétaires (des voitures). Ainsi, si on veut accéder au nom du propriétaire de la voiture voitureB :

voitureB.propriétaire.nom

Il est toujours possible d'ajouter une propriété à un objet qui aura déjà été défini. L'instruction qui suit, permet d'ajouter une propriété couleur à voitureA. En revanche cela n'affecte aucun des autres objets du même type, si on veut effectuer une modification générale, on modifiera la définition du type voiture.

voitureA.couleur = "voiture";

Utiliser la méthode Object.create

Il est également possible de créer des objets en utilisant la méthode Object.create. Cette méthode peut s'avérer utile pour choisir le prototype de l'objet qu'on veut créer, sans définir de constructeur. Voir la page sur Object.create pour plus d'informations.

Héritage

En JavaScript, tous les objets héritent d'au moins un autre objet. L'objet dont on hérite est appelé le prototype, les propriétés héritées sont présentes dans l'objet prototype du constructeur.

Indexer les propriétés d'un objet

Avec JavaScript 1.0, on peut faire référence à une propriété d'un objet par son nom ou par son indice. Avec les versions suivantes, en revanche, si initialement on définit une propriété par son nom, on doit toujours y faire référence par son nom et si, initialement, on la définit par son indice, on doit toujours y faire référence par son indice.

Cette contrainte existe lorsqu'on crée un objet et ses propriétés via un constructeur et que l'on définit les propriétés une par une, de façon explicite (ex. maVoiture.couleur = "verte"). Si on utilise un indice dans la définition initiale (comme maVoiture[5] = "25L"), on doit continuer de l'utiliser en y faisant référence avec maVoiture[5].

Les objets provenant du langage HTML n'obéissent pas à cette contrainte et on peut toujours utiliser les indices ou les noms des propriétés, par exemple pour les tableaux de formulaires : forms. L'indice correspondra à l'ordre dans lequel l'élément apparaît dans le document. Ainsi si la deuxième balise <FORM> d'un document possède un attribut NAME qui vaut "monFormulaire", on pourra y faire référence avec document.forms[1] ou document.forms["monFormulaire"] ou encore document.monFormulaire.

Définir les propriétés d'un type d'objet

Il est possible d'ajouter un propriété à un objet défini auparavant en utilisant la propriété prototype. Cela permet de définir une propriété qui est partagée par tous les objets d'un type donné et non pas seulement pour une seule instance de l'objet. Le code qui suit permet d'ajouter une propriété couleur à tous les objets du type voiture, ensuite, il assigne une valeur pour cette propriété couleur pour l'objet voiture1.

Voiture.prototype.couleur = null;
voiture1.couleur = "noir";

Voir la page sur la propriété prototype de l'objet Function pour plus d'informations.

Définir des méthodes

Une méthode est une fonction associée à un objet. Autrement dit, une méthode est une propriété d'un objet qui est une fonction. Les méthodes sont définies comme les fonctions classiques mais assignées à une propriété d'un objet. En voici quelques exemples :

nomObjet.nomMéthode = nom_de_fonction;

var monObjet = {
  maMéthode: function(params) {
    // ...faire un truc
  }
};

nomObject est un objet existant, nomMéthode le nom de la propriété à laquelle on assigne la fonction et nom_de_fonction le nom d'une fonction existante.

Il est possible d'appeler la méthode, dans le contexte de l'objet, avec la syntaxe suivante :

objet.nomMéthode(params);

On peut définir des méthodes  pour un type d'objet en ajoutant la définition d'une méthode dans le constructeur de l'objet. Ainsi, on peut définir une méthode qui permet d'afficher les propriétés définies auparavant pour le type d'objet voiture :

function afficheCarVoiture() {
  var result = "Une voiture de " + this.année + " construite par " + this.fabricant
    + " : " + this.modèle;
  pretty_print(result);
}

pretty_print est une fonction qui permet d'afficher une ligne horizontale puis une chaîne de caractères. Le mot-clé this permet de faire référence à l'objet auquel appartient la méthode.

Cette fonction peut devenir une méthode de voiture avec l'instruction suivante :

this.afficheCarVoiture = afficheCarVoiture;

La définition complète de voiture serait désormais :

function Voiture(fabricant, modèle, année, propriétaire) {
  this.fabricant = fabricant;
  this.modèle = modèle;
  this.année = année;
  this.propriétaire = propriétaire;
  this.afficheCarVoiture = afficheCarVoiture;
}

Il est ensuite possible d'appeler la méthode afficheCarVoiture pour chaque objet crée avec ce constructeur.

voiture1.afficheCarVoiture();
voiture2.afficheCarVoiture();

Utiliser this

JavaScript possède un mot-clé spécial, this, qui peut être utilisé au sein d'une méthode pour faire référence à l'objet courant. Si, par exemple, on dispose d'une fonction validate qui permet de valider la propriété value d'un objet en fonction de bornes inférieure et supérieure :

function validate(obj, borneinf, bornesup) {
  if ((obj.value < borneinf) || (obj.value > bornesup))
    alert("Valeur invalide !");
}

Cette fonction pourrait être utilisée pour chaque gestionnaire d'événement onchange en utilisant this pour passer l'élément en argument :

<input type="text" name="age" size="3"
  onChange="validate(this, 18, 99)">

En général, this fait référence à l'objet qui appelle la méthode.

Utilisé avec la propriété form, this peut faire référence au formulaire parent de l'objet courant. Dans l'exemple qui suit, le formulaire monFormulaire contient un objet Text et un bouton. Quand l'utilisateur clique sur le bouton, la valeur de l'objet Text est mise à jour avec le nom du formulaire. Le gestionnaire d'événement du bouton, onclick, utilise this.form pour faire référence à l'objet monFormulaire.

<form name="monFormulaire">
<p><label>Nom du formulaire :<input type="text" name="text1" value="Beluga"></label>
<p><input name="button1" type="button" value="Afficher le nom du formulaire"
     onclick="this.form.text1.value = this.form.name">
</p>
</form>

Définir des getters et setters

Un getter (du verbe anglais get, ici signifiant obtenir) est une méthode qui permet de récupérer la valeur d'une propriété donnée. Un setter (du verbe anglais set, ici signifiant définir) est une méthode permettant de définir (ou de mettre à jour) la valeur d'une propriété donnée. On peut définir des getters et des setters pour n'importe quel objet, prédéfini ou défini par l'utilisateur, qui supporte l'ajout de nouvelles propriétés. La syntaxe utilisée est celle d'un littéral objet.

JavaScript 1.8.1 note

Avec JavaScript 1.8.1, les setters ne sont plus invoqués pour définir les propriétés lorsqu'on utilise un initialisateur d'objet ou de tableau.

La session JS shell qui suit montre le fonctionnement de getters et setters pour un objet o. Avec Firefox, il est possible de lancer une console JavaScript en pressant Ctrl+Shift+K.

js> var o = {a: 7, get b() {return this.a + 1;}, set c(x) {this.a = x / 2}};
[object Object]
js> o.a;
7
js> o.b;
8
js> o.c = 50;
js> o.a;
25

Les propriétés de l'objet o sont :

  • o.a — un nombre
  • o.b — un getter qui renvoie o.a plus 1
  • o.c — un setter qui met à jour la valeur de o.a à la moitié de la valeur passée en paramètre de o.c

Il faut préciser que les noms des getters et setters d'un objet définis via un littéral objet qui utilise la syntaxe "[gs]et propriété()" (et non pas __define[GS]etter__ ) ne sont pas les noms des getters/setters même (malgré la syntaxe [gs]et nomPropriété(){ }). Pour nommer un fonction dans un getter ou un setter en utilisant la syntaxe "[gs]et propriété()", il faut explicitement définir une fonction en utilisant Object.defineProperty (ou  Object.prototype.__defineGetter__ qui est la méthode plus ancienne et qu'il est maintenant déconseillé d'utiliser).

La session JavaScript qui suit montre comment on peut ajouter des getters et des setters au prototype Date et ajouter une propriété year à toutes les instances de la classe Date. On utilise les méthodes, déjà existantes pour le type Date, getFullYear et setFullYear pour faire le getter et le setter :

js> var d = Date.prototype;
js> Object.defineProperty(d, "year", {
    get: function() {return this.getFullYear() },
    set: function(y) { this.setFullYear(y) }
});

Les instructions qui suivent montrent l'utilisation de ces getter et setter avec un objet Date :

js> var now = new Date;
js> print(now.year);
2000
js> now.year = 2001;
987617605170
js> print(now);
Wed Apr 18 11:13:25 GMT-0700 (Pacific Daylight Time) 2001

Des syntaxes obsolètes

Auparavant, JavaScript a supporté plusieurs syntaxes permettant de définir des getters et des setters : aucune de ces syntaxe ne fut supportée sur moteurs autres que SpiderMonkey et elles ne furent plus supportées depuis. Voir cette analyse des syntaxes disparues pour plus de détails à ce sujet et pour des méthodes d'adaptation envisageables (article en anglais).

Résumé

En règle générale, les getters et les setters sont :

  • soit définis en utilisant des initialisateurs d'objet,
  • soit ajoutés plus tard à n'importe quel objet, en utilisant une méthode permettant d'ajouter un getter ou un setter.

Lorsqu'on définit des getters et des setters en utilisant des initialisateurs d'objet, il suffit de préfixer la méthode de get pour un getter et de set pour un setter. La méthode du getter ne contient pas de paramètre, la méthode du setter devra contenir au moins un paramètre (la valeur à mettre à jour ou à définir) :

var o = {
  a: 7,
  get b() { return this.a + 1; },
  set c(x) { this.a = x / 2; }
};

Il est aussi possible d'ajouter des getters et des setters après la création de l'objet en utilisant la méthode Object.defineProperties. Cette méthode utilise deux arguments, le premier correspond à l'objet dont on veut définir le getter ou le setter, le second correspond à un objet dont les noms des propriétés sont les noms du getter ou du setter et les valeurs sont les objets définissant les fonctions getter ou setter. Voici un exemple qui définit les mêmes getter et setter que l'exemple précédent :
 

var o = { a:0 }

Object.defineProperties(o, {
    "b": { get: function () { return this.a + 1; } },
    "c": { set: function (x) { this.a = x / 2; } }
});

o.c = 10 // Utilise le setter et assigne 10 / 2 (5) à 'a'
console.log(o.b) // Utilise le getter et renvoie a + 1 soit 6

La façon qu'on utilise dépendra avant tout de votre style de programmation et du problème qu'on doit résoudre. Si on utilise plus souvent un initialisateur quand on définit un prototype, on utilisera la première syntaxe qui peut être plus compacte et naturelle. En revanche, si on veut ajouter un getter ou un setter une fois l'objet défini (sur lequel on ne maîtrise pas la création) alors la seconde syntaxe est la seule alternative possible. Cette seconde syntaxe traduit mieux l'aspect dynamique de JavaScript mais peut être moins lisible.

Avant Firefox 3.0, les getters et setters n'étaient pas supportés pour les éléments du DOM. Les versions plus anciennes tombaient en erreur, silencieusement (c'est à dire sans alerter l'utilisateur). Si vous avez besoin de gérer des exceptions pour ces cas, il faut changer le prototype de HTMLElement (HTMLElement.prototype.__define[SG]etter__) pour lancer des exceptions.

Avec Firefox 3.0, la définition d'un getter ou d'un setter avec le nom d'une propriété déjà existante déclenchera une exception. La propriété en question doit être supprimée auparavant. Les anciennes versions de Firefox ne gèrent pas ce cas.

 

Voir aussi

Supprimer des propriétés

Il est possible de supprimer des propriétés non-héritées en utilisant l'opérateur delete. Le code qui suit fournit un exemple :

//Créer un objet monObj, avec deux propriétés, a et b.
var monObj = new Object;
monObj.a = 5;
monObj.b = 12;

//Supprimer la propriété a, il ne reste que la propriété b.
delete monObj.a;
console.log ("a" in monObj) // renvoie "false"

Il est aussi possible d'utiliser delete pour supprimer une variable globale si le mot-clé var n'avait pas été utilisé pour la déclarer.

g = 17;
delete g;

Voir la page sur l'opérateur delete pour plus d'informations.

Voir aussi

Étiquettes et contributeurs liés au document

Étiquettes :
Contributeurs ayant participé à cette page : SphinxKnight
Dernière mise à jour par : SphinxKnight,