Les propriétés des objets en Javascript
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 objet javascript 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 est 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 ) {
}
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];
}
Retenez 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 vous dites, probablement à 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ée un peu plus haut.
Un attribut non configurable (configurable=false) ne peut être retiré d’un objet. 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. Ensuite définissez 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, il n’y a plus d’erreur.
Les propriétés spécifiques aux accesseurs
Les accesseurs ont aussi deux propriétés additionnelles que sont get et set.
person.name = "James";
console.log(person.name); // Affiche ‘James’
person.name = "Bob";
console.log(person.name); // Affiche ‘Bob’
delete person.name; // Génère une erreur
Object.defineProperties et Object.defineProperty
En javascript, il est possible.
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 comporte 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écifie 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;
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"; console.log(person.name); // Affiche 'James' delete person.name;
Tout est à peu près dit sur les propriétés des objets en javascript, gardez bien tout cela à l’esprit, ça vous évitera des surprises ou encore des comportements de code incompréhensibles.