Il y a deux sortes de proriétés dâobjet.
Le premier type est les propriétés de données. Nous savons déjà comment travaillez avec. Toutes les propriétés que nous avons utilisés jusquâà maintenant étaient des propriétés de données.
Le second type de propriété est quelque chose de nouveau. Câest un accesseur de propriété. Ce sont essentiellement des fonctions qui exécutent une récupération ou une déclaration de valeur, mais qui ressemblent à une propriété normale pour le code extérieur.
Getters et Setters
Les accesseurs de propriétés sont représentés par des méthodes âgetterâ et âsetterâ. Dans un objet littéral elles se demarquent par get et set :
let obj = {
get propName() {
// Getter, le code va récupérer obj.propName
},
set propName(value) {
// Setter, le code va définir obj.propName = value
}
};
Le getter fonctionne quand obj.propName est lu, le setter â quand il sâagit dâune assignation.
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 lâinformation existante, donc nous pouvons implémenter un accesseur :
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
}
};
alert(user.fullName); // John Smith
De lâextérieur, un accesseur de propriété ressemble à une propriété normale. Câest lâidée dâun accesseur. Nous nâappellons pas user.fullName comme une fonction, nous la lisons normalement : le getter agit en arrière plan.
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"; // Erreur (la propriété n'a qu'un getter)
Corrigeons cela en ajoutant un setter pour user.fullName :
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
},
set fullName(value) {
[this.name, this.surname] = value.split(" ");
}
};
// Le setter est exécuté avec la valeur donnée.
user.fullName = "Alice Cooper";
alert(user.name); // Alice
alert(user.surname); // Cooper
Comme résultat, nous avons une propriété âvirtuelleâ fullName. Elle est lisible et ecrivable.
Descripteurs dâaccesseur
Les descripteurs dâaccesseur de propriété sont différents de ceux pour les propriété de données.
Pour les accesseurs de propriétés, il nây a pas de value ou writable, à la place il y a les fonctions get et set.
Un descripteur dâaccesseur peut avoir :
getâ une fonction sans arguments, pour la lecture de propriété,setâ une fonction avec un argument, qui fonctionne lorsque la propriété change de valeur,enumerableâ identique aux propriétés de donnéesconfigurableâ identique aux propriétés de données
Par exemple, pour créer un accesseur fullName avec defineProperty, on peut 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) {
[this.name, this.surname] = value.split(" ");
}
});
alert(user.fullName); // John Smith
for(let key in user) alert(key); // name, surname
Veuillez notez quâune propriété peut être soit un accesseur (qui a les méthodes get/set) ou une propriété de données (qui a value), pas les deux.
Si nous essayons de fournir les deux get and value dans le même descripteur, il y aura une erreur :
// Erreur : Descripteur de propriété invalide.
Object.defineProperty({}, 'prop', {
get() {
return 1
},
value: 2
});
Des getters/setters plus intelligents
Les Getters/setters peuvent être utilisés comme des enveloppes autour des âréellesâ valeurs de propriété pour gagner plus de contrôles sur leurs opérations.
Par exemple, si nous voulions interdire les noms trop court pour user, nous pourrions 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); // Pete
user.name = ""; // Le nom est trop court...
Donc, le nom est stocké dans la propriété _name, et lâaccés est fait par le getter et le setter.
Techniquement, le code extérieur est capable dâaccéder directement à la propriété en utilisant user._name. Mais il y a une convention très connue, selon laquelle les propriétés commençant par un underscore "_" sont internes et ne devraient pas être touchées depuis lâextérieur des objets.
Utilisation pour la compatibilité
Un des avantages de lâutilisation des accesseurs et quâils permettent de prendre le contrôle sur un propriété de données ânormaleâ à tout moment, en la remplaçant par un getter et un setter et modifiant son comportement.
Imaginons que nous commencions des objets utilisateur en utilisant des 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 pourraient changer. Au lieu dâage on pourrait decider 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 fait-on avec lâancien code qui utilise toujours la propriété age ?
On peut essayer de trouver tous les endroits où on utilisent age et les modifier, mais ça prend du temps et ça peut être compliqué à faire si le code est utilisé par plusieurs personnes. En plus, age est une bonne chose à avoir dans user, nâest ce pas ?
Gardons-le.
Ajoutons un getter pour age et résolvons le problème :
function User(name, birthday) {
this.name = name;
this.birthday = birthday;
// age est calculé à partir de la date actuelle et de 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 est disponible
alert( john.age ); // ...Ainsi que l'age
Maintenant lâancien code fonctionne toujours et nous avons une propriété additionnelle.
Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâ¦)