Troisème article de la série consacrée à javascript et ses fondamentaux. Dans cet article, je vous parle des propriétés des objets.

Pré-requis

Les pré-requis ne sont pas indispensables mais conseillés:
– l’article sur les types fondamentaux en javascript;
– l’article sur les fonctions en javascript.

Tester si une propriété d’un objetjavascript existe

Dans le premier article de la série, j’évoque la possibilité que permet javascript d’ajouter et retirer des propriétés à la volée. Il peut être parfois nécessaire de vérifier si une propriété d’un objet existe ou pas. Le meilleur moyen de le faire est d’utiliser l’opérateur in. Ceci est valable non seulement pour les propriétés mais aussi pour les méthodes qui sont aussi en quelque sorte des propriétés.

let person = {
  age : 8
}
if ( "age" in person ) {
  // faire quelque chose
}

Supprimer une propriété d’un objet javascript existe

C’est avec l’instruction delete que vous retirez une propriété d’un objet. Idem pour les méthodes.

let person = {
  age : 8
}
delete person.age;

Boucler sur les propriétés d’un objet

Par défaut, toutes les propriétés que vous ajoutez à un objet sont enumerable (attribut Enumerable de la propriété fixé à true). La boucle for-in boucle sur toutes les propriétés enumerable. Une petite limite, cette boucle fonctionne tant que la propriété suivante est enumerable: si vous avez 3 propriétés p1 enumerable, p2 non enumerable et p3 enumerable, la boucle s’arrête au premier élément puis que la suivante est non enumerable.

for (let property in object) {
  console.log("Name: " + property;
  console.log("Value: " + object[property];
}

Une autre solution, utiliser Object.keys() qui renvoie un tableau comportant les propriétés, mais uniquement les propriétés enumerable de l’objet, pas celles du sous-objet javascript prototype.

let person = {
  nom : "toto",
  age : 8
}
var properties = Object.keys(person);
for (let i=0; let len=properties.length;i<len;i++) {
  console.log("Name: " + properties[i];
  console.log("Value: " + object[properties[i]];
}

Gardez à l’esprit que toutes les propriétés d’un objet ne sont pas enumerable: la plupart des propriétés natives d’un objet ne le sont pas. Prenez par exemple, la propriété length d’un objet array ne l’est pas. Pour savoir si une propriété est enumerable ou pas, il suffit d’invoquer la méthode propertyIsEnumerable.

let person = {
  nom : "toto",
  age : 8
}
var properties = Object.keys(person1);
console.log("length" in properties);  // affiche true, la propriété existe
console.log(properties.propertyIsEnumerable("length");  // affiche false, la propriété n'est pas enumerable

Accesseur et modificateur d’un objet en javascript

Pour les habitués des langages objets, il est courant de définir des propriétés privées (private ou protected) d’un objet et des accesseurs/modificateurs pour y accéder en lecture/écriture (get/set). Cela interdit tout accès direct à la propriété. Je vois souvent cet usage systématique sans discernement que je trouve d’un intérêt discutable. Il faut qu’il y ait une valeur ajoutée à cette pratique.

Bref, en javascript, c’est aussi possible. Comme dit jusqu’à présent, les objets javascript sont constitués de propriétés sous forme de paires clé/valeur qui sont des données ou des fonctions. Il existe un deuxième type de propriété en javascript: les accesseurs. Au lieu de stocker des données ou des pointeurs, ce type permet de définir une fonction appelée lorsque la propriété est lue.

Dans l’exemple ci-dessous, sont définis accesseur get et modificateur set sur la propriété privée _name (‘_’ est une convention pour définir des propriétés privées d’un objet). Cependant, l’appel du setter et du getter se fait par la propriété sans le ‘_’ (person.name). get et set se déclarent comme des fonctions avec un corps de fonction mais sans le mot clé function. get retourne une valeur et set renvoie une valeur.

let person = {
  _name : "Bob",

  get name() {
    return this_name;
  },

  set name(value) {
    this_name = value;
  }
}
console.log(person.name); // affiche 'Bob'
person.name = "Robert";
console.log(person.name); // affiche 'Robert'
person._name = "Richard";
console.log(person.name); // affiche 'Richard'
console.log(person._name); // affiche 'Richard'

Remarquez que cette syntaxe n’interdit en rien l’appel à la propriété directement (person._name). Et vous allez probablement vous dire à juste raison, quel intérêt ? En effet, il n’y a aucune raison de passer par des getter ou setter juste pour stocker ou retourner une valeur. getter et setter prennent sens si des traitements sont nécessaires avant de renvoyer une valeur (un calcul par exemple) ou si la modification d’une valeur doit déclencher des comportements particuliers.

Les attributs d’une propriété

javascript permet de créer des attributs avec les mêmes propriétés que les attributs intégrés à tous les objets lors de leur création.

Les propriétés communes aux données et aux accesseurs

Deux propriétés à valeur booléenne : Enumerable qui détermine si la propriété est énumérable et Configurable qui détermine les conditions de modification. Par défaut, toutes les propriétés que vous créez sont énumérables et configurables. Je ne reviens pas sur la propriété Enumerable dont j’ai parlé un peu plus haut.

Un attribut non configurable (configurable=false) ne peut être retirée d’un objet et vous ne pouvez rendre configurable un attribut non configurable. Attention, ceci n’est valable que dans le mode strict (“use strict” déclaré en début de script qui devrait être la norme).

Enfin la définition des propriétés d’un attribut passe par l’instruction Object.defineProperty(monObjet,”attribut”, {…}).

"use strict";
let person = {
  name : "James"
}
Object.defineProperty(person,"name", {
  configurable : false
})
console.log(person.name); // Affiche 'James'
person.name = "Bob";
console.log(person.name); // Affiche 'Bob'
delete person.name; // Génère une erreur

Les propriétés spécifiques aux données

Deux s’ajoutent aux précédentes : value et writable. La première spécifie la valeur de l’attribut, la seconde indique si cette valeur est modifiable ou pas.

Ecrire ceci

let person = {
  name : "James"
}

est équivalent à écrire cela

Object.defineProperty(person,"name", {
  value : "James",
  enumerable : true,
  configurable : true,
  writable : true
})

Rien ne vous interdit d’utiliser la première forme littérale pour créer votre objet puis ensuite définir chacune des propriétés autres que value (aucun intérêt à redéfinir la valeur) avec la deuxième forme. En mode strict, si vous essayez de modifier la valeur d’une propriété non writable, une erreur est levée.

Les propriétés spécifiques aux accesseurs

Les accesseurs ont aussi deux propriétés additionnelles que sont get et set

console.log(person.name); // Affiche ‘James’
person.name = “Bob”;
console.log(person.name); // Affiche ‘Bob’
delete person.name; // Génère une erreur
[/javascript]

Object.defineProperties et Object.defineProperty

En javascript, il est possiblde

Interroger l’état des propriétés d’un attribut

En javascript, l’instruction Object.getOwnPropertyDescriptor appliquée à un objet A renvoie un objet B Descriptor conportant les propriétés enumerable, configurable, writable et value relatives à l’objet A.

let person = {
  name : "James"
}
let descriptor = Object.getOwnPropertyDescriptor(person, "name");
console.log(descriptor.enumerable); // Affiche 'true'
console.log(descriptor.configurable); // Affiche 'true'
console.log(descriptor.writable); // Affiche 'true'
console.log(descriptor.value); // Affiche 'James'

Sécuriser les modifications d’objet

Les objets javascript, tout comme les attributs, possèdent aussi des propriétés internes. L’une d’elles est la propriété booléenne Extensible qui spécifier si un objet est modifiable (extensible) ou pas. Par défaut, tous les objets que vous créez sont extensibles : ce qui laisse la possibilité de leur ajouter des attributs.

Rendre un objet non extensible (pas d’ajout d’attributs)

Avec l’instruction Object.preventExtensions(monObjet), il n’est plus possible d’ajouter des attributs mais il reste possible d’en retirer.
En mode strict, une erreur est levée. En mode non strict, aucune erreur mais l’ajout de la propriété ne fonctionne pas.

let person = {
  name : "James"
}
console.log(Object.isExtensible(person)); // Affiche 'true'
Object.preventExtensions(person);
console.log(Object.isExtensible(person)); // Affiche 'false'
person.reflect = function() {
  console.log(this.name);
}
console.log("reflect" in person); // Affiche 'false'

Rendre un objet non extensible et non configurable (ni ajout, ni retrait d’attributs)

Avec l’instruction Object.seal(monObjet), il n’est plus possible ni d’ajouter des attributs (objet non extensible) ni d’en retirer (objet non configurable). Il n’est plus possible non plus de changer leur type (données <-> accesseur). Par contre un object sealed n’empêche pas de modifier les valeurs des attributs. Par défaut, tous les objets que vous créez ne sont pas sealed. En mode strict, une erreur est levée si vous utilisez une instruction non autorisée.

let person = {
  name : "James"
}
console.log(Object.isExtensible(person)); // Affiche 'true'
console.log(Object.isSealed(person)); // Affiche 'false'
Object.seal(person);
console.log(Object.isExtensible(person)); // Affiche 'false'
console.log(Object.isSealed(person)); // Affiche 'true'

person.reflect = function() {
  console.log(this.name);
}
console.log("reflect" in person); // Affiche 'false'
delete person.name;  // Pas possible
console.log("name" in person); // Affiche 'true'
let descriptor = Object.getOwnPropertyDescriptor(person, "name");
console.log(descriptor.configurable); // Affiche 'false'

Rendre un objet non modifiable

D’un usage limité voire rare, on entend par objet non modifiable, l’impossibilité d’ajouter ou retirer des attributs mais aussi l’impossibilité de modifier les valeurs des propriétés. Cela passe par l’instruction Object.freeze(monObjet). En mode strict, une erreur est levée si vous utilisez une instruction non autorisée.

let person = {
  name : "James"
}
console.log(Object.isExtensible(person)); // Affiche 'true'
console.log(Object.isSealed(person)); // Affiche 'false'
console.log(Object.isFrozen(person)); // Affiche 'false'
Object.freeze(person);
console.log(Object.isExtensible(person)); // Affiche 'false'
console.log(Object.isSealed(person)); // Affiche 'true'
console.log(Object.isFrozen(person)); // Affiche 'true'

person.reflect = function() {
  console.log(this.name);
}
console.log("reflect" in person); // Affiche 'false'

person.name = "Roger";  // Pas possible
console.log(person.name); // Affiche 'James'

delete person.name;  // Pas possible
console.log("name" in person); // Affiche 'true'
Tout a été à peu près dit sur les propriétésjavascript, gardez bien tout cela à l’esprit, ça vous évitera des surprises ou encore des comportements de code incompréhensibles.

 

Si vous avez aimé cet article, partagez le.

 

Si vous constatez des coquilles, ou avez des remarques à faire ou encore souhaitez manifester votre satisfaction de ce tuto, n’hésitez pas les commentaires sont faits pour ça.