Il existe deux sortes de propriétés d’objets.

La première sorte est celle des propriétés de données. Nous savons déjà comment travailler avec elles. Toutes les propriétés que nous avons utilisées jusqu’à présent étaient des propriétés de données.

Le deuxième type de propriétés est quelque chose de nouveau. Il s’agit des propriétés d’accesseur. Ce sont essentiellement des fonctions qui s’exécutent sur l’obtention et la définition d’une valeur, mais qui ressemblent à des propriétés régulières pour un code externe.

Getters and setters

Les propriétés accesseurs sont représentées par des méthodes « getter » et « setter ». Dans un littéral objet, elles sont dénotées par get et set:

let obj = { get propName() { // getter, the code executed on getting obj.propName }, set propName(value) { // setter, the code executed on setting obj.propName = value }};

Le getter fonctionne lorsque obj.propName est lu, le setter – lorsqu’il est assigné.

Par exemple, nous avons un objet user avec name et surname:

let user = { name: "John", surname: "Smith"};

Maintenant nous voulons ajouter une propriété fullName, qui devrait être "John Smith". Bien sûr, nous ne voulons pas copier-coller des informations existantes, donc nous pouvons l’implémenter comme un accesseur :

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }};alert(user.fullName); // John Smith

De l’extérieur, une propriété accesseur ressemble à une propriété ordinaire. C’est l’idée des propriétés accesseurs. Nous n’appelons pas user.fullName comme une fonction, nous le lisons normalement : le getter s’exécute en coulisse.

Pour l’instant, fullName n’a qu’un getter. Si nous essayons d’assigner user.fullName=, il y aura une erreur:

let user = { get fullName() { return `...`; }};user.fullName = "Test"; // Error (property has only a getter)

Réparons cela en ajoutant un setter pour user.fullName :

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }, set fullName(value) { = value.split(" "); }};// set fullName is executed with the given value.user.fullName = "Alice Cooper";alert(user.name); // Alicealert(user.surname); // Cooper

En conséquence, nous avons une propriété « virtuelle » fullName. Elle est lisible et inscriptible.

Descripteurs d’accesseur

Les descripteurs des propriétés d’accesseur sont différents de ceux des propriétés de données.

Pour les propriétés d’accesseur, il n’y a pas de value ou de writable, mais plutôt des fonctions get et set.

C’est-à-dire qu’un descripteur d’accesseur peut avoir :

  • get – une fonction sans argument, qui fonctionne lorsqu’une propriété est lue,
  • set – une fonction avec un argument, qui est appelée lorsque la propriété est définie,
  • enumerable – comme pour les propriétés de données,
  • configurable – comme pour les propriétés de données.

Par exemple, pour créer un accesseur fullName avec defineProperty, nous pouvons passer un descripteur avec get et set :

let user = { name: "John", surname: "Smith"};Object.defineProperty(user, 'fullName', { get() { return `${this.name} ${this.surname}`; }, set(value) { = value.split(" "); }});alert(user.fullName); // John Smithfor(let key in user) alert(key); // name, surname

Veuillez noter qu’une propriété peut être soit un accesseur (a get/set méthodes) ou une propriété de données (a value), pas les deux.

Si on essaie de fournir à la fois get et value dans le même descripteur, il y aura une erreur :

// Error: Invalid property descriptor.Object.defineProperty({}, 'prop', { get() { return 1 }, value: 2});

Des getters/setters plus intelligents

Les getters/setters peuvent être utilisés comme enveloppes sur les valeurs de propriété « réelles » pour obtenir plus de contrôle sur les opérations avec elles.

Par exemple, si nous voulons interdire les noms trop courts pour user, nous pouvons avoir un setter name et garder la valeur dans une propriété séparée _name:

let user = { get name() { return this._name; }, set name(value) { if (value.length < 4) { alert("Name is too short, need at least 4 characters"); return; } this._name = value; }};user.name = "Pete";alert(user.name); // Peteuser.name = ""; // Name is too short...

Donc, le nom est stocké dans la propriété _name, et l’accès se fait via un getter et un setter.

Techniquement, le code externe est capable d’accéder directement au nom en utilisant user._name. Mais il existe une convention largement connue selon laquelle les propriétés commençant par un soulignement "_" sont internes et ne doivent pas être touchées de l’extérieur de l’objet.

Utilisation pour la compatibilité

L’une des grandes utilisations des accesseurs est qu’ils permettent de prendre le contrôle d’une propriété de données « régulière » à tout moment en la remplaçant par un getter et un setter et de modifier son comportement.

Imaginez que nous avons commencé à implémenter des objets utilisateurs en utilisant les propriétés de données name et age:

function User(name, age) { this.name = name; this.age = age;}let john = new User("John", 25);alert( john.age ); // 25

…Mais tôt ou tard, les choses peuvent changer. Au lieu de age, nous pouvons décider de stocker birthday, parce que c’est plus précis et plus pratique:

function User(name, birthday) { this.name = name; this.birthday = birthday;}let john = new User("John", new Date(1992, 6, 1));

Maintenant, que faire avec l’ancien code qui utilise encore la propriété age ?

Nous pouvons essayer de trouver tous ces endroits et les corriger, mais cela prend du temps et peut être difficile à faire si ce code est utilisé par beaucoup d’autres personnes. Et en plus, age est une chose agréable à avoir dans user, non ?

Maintenons-le.

Ajouter un getter pour age résout le problème :

function User(name, birthday) { this.name = name; this.birthday = birthday; // age is calculated from the current date and birthday Object.defineProperty(this, "age", { get() { let todayYear = new Date().getFullYear(); return todayYear - this.birthday.getFullYear(); } });}let john = new User("John", new Date(1992, 6, 1));alert( john.birthday ); // birthday is availablealert( john.age ); // ...as well as the age

Maintenant l’ancien code fonctionne aussi et nous avons une belle propriété supplémentaire.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.