Maîtriser la manipulation fine des propriétés avec Object.defineProperty()

Assignation classqiue vs Définition contrôlée

En JavaScript, la méthode la plus courante pour ajouter une propriété à un objet est l'assignation directe via le point ou les crochets. Cependant, cette méthode offre peu de contrôle sur le comportement interne de la propriété.

let utilisateur = {};
utilisateur.pseudo = "DevMax";
utilisateur["role"] = "Admin";

console.log(utilisateur.pseudo); // DevMax
console.log(utilisateur); // {pseudo: 'DevMax', role: 'Admin'}

Pour un contrôle granulaire (lecture seule, masquage lors de l'énumération, etc.), JavaScript propose la méthode statique Object.defineProperty().

Syntaxe et paramètres

La méthode prend trois arguments distincts :

Object.defineProperty(objet, propriete, descripteur)
  • objet : La cible sur laquelle définir la propriété.
  • propriete : Une chaîne de caractères ou un symbole représentant le nom de la propriété.
  • descripteur : Un objet définissant la configuration et le comportement de la propriété.

Les types de decsripteurs de propriétés

Il existe deux types principaux de descripteurs : les descripteurs de données et les descripteurs d'accès. Un descripteur ne peut appartenir qu'à une seule de ces catégories.

1. Le descripteur de données

Ce descripteur définit une propriété qui possède une valeur physique. Il utilise principalement deux attributs spécifiques : value et writable.

const voiture = {};

Object.defineProperty(voiture, 'serie', {
  value: 'V-9982',
  writable: false, // Empêche toute modification ultérieure
  configurable: true
});

voiture.serie = 'V-0000'; 
console.log(voiture.serie); // Affiche toujours 'V-9982' en mode non-strict

2. Le descripteur d'accès (Getter & Setter)

Plutôt que de stocker une valeur, ce descripteur utilise des fonctions pour récupérer (get) ou modifier (set) une information. Il est interdit d'utiliser value ou writable avec ce type de descripteur.

const compteBancaire = { _solde: 1000 };

Object.defineProperty(compteBancaire, 'solde', {
  get: function() {
    console.log("Lecture du solde...");
    return `${this._solde} €`;
  },
  set: function(nouveauMontant) {
    if (nouveauMontant < 0) {
      console.error("Solde négatif interdit");
    } else {
      this._solde = nouveauMontant;
    }
  }
});

compteBancaire.solde = 1500;
console.log(compteBancaire.solde); // Lecture du solde... -> 1500 €

Attributs partagés par tous les descripteurs

Indépendamment du type (données ou accès), deux attributs fondamentaux régissent la visibilité et la structure de la propriété :

  • enumerable : Si true, la propriété apparaît lors d'une boucle for...in ou avec Object.keys(). Par défaut à false avec defineProperty.
  • configurable : Si false, le type de descripteur ne peut pas être modifié et la propriété ne peut pas être supprimée de l'objet.
const produit = {};

Object.defineProperty(produit, 'id', {
  value: 'SKU-500',
  enumerable: false, // Caché lors de l'itération
  configurable: false // Impossible de supprimer ou redéfinir
});

produit.nom = "Clavier";

console.log(Object.keys(produit)); // ['nom'] (l'id est absent)
delete produit.id; 
console.log(produit.id); // 'SKU-500' (la suppression a échoué)

Tableau des valeurs par défaut

Il est crucial de noter que les valeurs par défaut diffèrent selon que la propriété est créée par assignation ou via Object.defineProperty().

Attribut Défaut via assignation (obj.p = 1) Défaut via defineProperty()
value / get / set La valeur assignée undefined
writable true false
enumerable true false
configurable true false

Cas particulier : configurable vs writable

Une nuance importante existe : si configurable est à false, il est tout de même possible de changer la valeur de la propriété si et seulement si writable est à true.

const systeme = {};

Object.defineProperty(systeme, 'version', {
  value: '1.0.0',
  writable: true,
  configurable: false
});

// Autorisé car writable est true
systeme.version = '1.1.0';

// Autorisé via defineProperty car writable est true (on peut passer writable à false, mais pas l'inverse)
Object.defineProperty(systeme, 'version', { value: '1.2.0' });

console.log(systeme.version); // 1.2.0

Étiquettes: JavaScript ecmascript Object-Oriented web-development

Publié le 2 août à 06h42