es5-property-descriptor.png

ES5 - Le property descriptor

par Romain · · 0 commentaire(s)

Rappel ES5 - Le property descriptor

Dans une volonté d'en apprendre plus sur javascript, bien souvent nous partons sur l'apprentissage d'un nouveau framework, ces frameworks souvent accompagnés de cli permettent directement de pouvoir travailler en EcmaScript 2015+ (à travers babel) afin d'obtenir des applications "modernes".

Cependant, je remarque que la plupart du temps il est facile d'appliquer des principes et d'effectuer des tâches en javascript sans pour autant comprendre ce qu'il se passe sous le capot, d'autre part dans un cadre pédagogique où l'on souhaiterait enseigner ces nouveaux concepts à des personnes qui ont pratiqués du javascript à l'ancienne, il est bien difficile de pouvoir créer des analogies si les concepts ES5 sont inconnus.

Cet article est le premier d'une longue série afin de faire une piqure de rappel sur l'ancienne syntaxe de javascript que l'on appelle ES5 et qui a mon sens il est important de maitriser.

Le descripteur de propriété (Property Descriptor)

Le descripteur de propriété, qu'est-ce que c'est ?

Comme on a pu le constater les propriétés d'un objet ne sont pas uniquement un nom et une valeur, chaque propriété possède aussi un descripteur de propriété, comment pouvons-nous le voir alors ?

Nous pouvons l'obtenir grâce à la fonction getOwnPropertyDescriptor de Object

"use strict";

var personne1 = {
  nom: "Barraud",
  prenom: "Romain",
};

console.log(Object.getOwnPropertyDescriptor(personne1, "nom"));
/**
 * configurable: true
 * enumerable: true
 * value: "Barraud"
 * writable: true
 */

On voit qu'en plus de sa valeur , on a trois autres attributs qui répondent aux questions suivantes :

  • writable: Est-ce que l'on peut modifier la propriété ?
  • enumerable: Est-ce que l'on peut parcourir cette propriété ?
  • configurable: Est-ce que l'on peut peut modifier le property descriptor ?

Remarque : Ce descripteur est également de type object, on remarque chacune de ces 3 propriétés sont toutes alimentés avec true ce qui est le comportement par défaut lorsque l'on ne spécifie pas leur valeur

La propriété Writable du descripteur de propriété

Il est possible de changer la valeur de la propriété writable du descripteur de propriété afin d'empêcher celle-ci d'être modifiée par la suite. Pour cela nous allons modifier la propriété writable en lui donnant la valeur false.

"use strict";

var person = {
  name: "Doe",
  firstName: "John",
};

Object.defineProperty(person, "name", { writable: false });
console.log(Object.getOwnPropertyDescriptor(person, "name"));
/**
 * {
 *   configurable: true,
 *   enumerable: true,
 *   value: "Doe",
 *   writable: false
 * }
 */

Maintenant essayons de changer la propriété name de notre person

"use strict";

var person = {
  name: "Doe",
  firstName: "John",
};

Object.defineProperty(person, "name", { writable: false });
person.name = "Toh";
console.log(Object.getOwnPropertyDescriptor(person, "name"));

/*Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
    at <anonymous>:10:13*/

Remarque : La modification va échouer silencieusement à part si on est en 'strict mode' ce qui est notre cas

La propriété name est de type string, que se passe-t'il lorsque l'on modifie la propriété writable du descripteur de propriété, pour une propriété de type object ?

"use strict";

var person = {
  name: { first: "John", last: "Doe" },
};

Object.defineProperty(person, "name", { writable: false });
person.name = "Joey";
console.log(Object.getOwnPropertyDescriptor(person, "name"));

De la même manière il est impossible de réassigner name, qui est maintenant en lecture seule. Toutefois attention, il est possible de modifier l'objet vers lequel il pointe

"use strict";

var person = {
  name: { first: "John", last: "Doe" },
};

Object.defineProperty(person, "name", { writable: false });
person.name.first = "Joey";
console.log(person);

Remarque : Si on crée une propriété readonly, si elle contient un objet, alors cet objet est quand même modifiable, si on veut éviter cela nous pouvons utiliser la fonction freeze pour empêcher les modifications de l'objet.

"use strict";

var person = {
  name: { first: "John", last: "Doe" },
};

Object.defineProperty(person, "name", { writable: false });
Object.freeze(person.name);
person.name.first = "Joey";
console.log(person);

La propriété Enumerable du descripteur de propriété

Lorsque nous déclarons un objet nous avons la possibilité de pouvoir itérer sur leurs propriétés. Ce comportement est possible parce que par défaut la propriété enumerable de notre propriété est à `true``

Il est donc possible de modifier enumerable pour que notre propriété n'apparaisse plus lors de l'itération.

"use strict";

var person = {
  name: { first: "John", last: "Doe" },
  age: 30,
};

Object.defineProperty(person, "name", { enumerable: false });

for (var propertyName in person) {
  console.log(propertyName + " : " + person[propertyName]);
}

Dans notre code, on remarque que la propriété name ne s'affiche plus.

Attention : Mettre un property enumerable cache également la propriété vis à vis d'autres fonctions

"use strict";

var person = {
  name: { first: "John", last: "Doe" },
  age: 30,
};

Object.defineProperty(person, "name", { enumerable: false });

console.log(Object.keys(person)); // pareil pour Object.entries() etc...

Il faut savoir que cela affecte également la serialisation de l'objet, dans le cas où vous souhaiteriez qu'une propriété ne soit pas serialisée, il suffirait de modifier la propriété enumerable à false avant sa sérialisation. Malgré tout cela on peut quand même aller chercher la propriété.

"use strict";

var person = {
  name: { first: "John", last: "Doe" },
  age: 30,
};

Object.defineProperty(person, "name", { enumerable: false });
console.log(JSON.stringify(person)); // {"age": 30}

console.log(person.name); // { first: "John", last: "Doe" }

La propriété Configurable du descripteur de propriété

la propriété configurable va verrouiller une propriété pour la prévenir d'etre changée et elle prévient aussi la propriété d'être supprimée de l'objet.

"use strict";

var person = {
  name: { first: "John", last: "Doe" },
  age: 30,
};
Object.defineProperty(person, "name", { configurable: false });
Object.defineProperty(person, "name", { enumerable: false }); // Cannot redefine property : name

On ne peut plus redéfinir la propriété enumerable de name Ce qui est interessant avec configurable c'est qu'elle ne peut plus être configurable à nouveau !

  "use strict";

  var person = {
    name: { first: "John", last: "Doe" },
    age: 30,
  };
  Object.defineProperty(person, "name", { configurable: false });
  Object.defineProperty(person, "name", { configurable: true }); // Cannot redefine property : name

Remarque: Vous pouvez toutefois changer l'attribut writable à false mais vous ne pourrez plus le remettre à true par la suite

"use strict";

var person = {
  name: { first: "John", last: "Doe" },
  age: 30,
};
Object.defineProperty(person, "name", { configurable: false });
Object.defineProperty(person, "name", { writable: false });
Object.defineProperty(person, "name", { writable: true }); // Cannot redefine property : name

Impossible de supprimer une property si configurable : false

"use strict";

var person = {
  name: { first: "John", last: "Doe" },
  age: 30,
};
Object.defineProperty(person, "name", { configurable: false });
delete person.name;
/**
 * 
 * VM1026:9 Uncaught TypeError: Cannot delete property 'name' of #<Object>
 * at <anonymous>:9:1
 */ 

Pour résumer si configurable : false

  • Vous ne pouvez plus changer l'attribut enumerable.
  • Vous ne pouvez plus changer l'attribut configurable.
  • Vous ne pouvez plus changer l'attribut writable à true.
  • Vous ne pouvez plus supprimer la propriété.

Voilà pour le petit topo sur le property descriptor en javascript, si vous souhaitez aller plus loin voici une liste d'articles connexes: