Skip to content

Commit afa872f

Browse files
authored
Merge pull request #343 from Pat6aC/property-accessors
Property accessors
2 parents 51d53c1 + 993d5b3 commit afa872f

File tree

1 file changed

+244
-0
lines changed

1 file changed

+244
-0
lines changed

property-accessors.md

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# Propriété opérateurs de lecture et d'écriture
2+
3+
Il existe deux types de propriétés d'objet.
4+
5+
Le premier désigne *les propriétés de données*. Nous savons déjà comment les utiliser. Toutes les propriétés que nous avons utilisées jusqu'à maintenant étaient des propriétés de données.
6+
7+
Le second type de propriétés est quelque chose de nouveau. Ce sont les *propriétés des accesseurs*. Ce sont essentiellement des fonctions qui s'exécutent lors de la lecture d'une valeur ou son écriture, mais elles ressemblent à des propriétés classiques pour un code externe.
8+
9+
## Les opérateurs de lecture et d'écriture
10+
11+
Les propriétés des accesseurs sont représentées par les méthodes "lecture" et "écriture". Dans un objet litéral ils sont notés comme`get` et `set`:
12+
13+
```js
14+
let obj = {
15+
*!*get propName()*/!* {
16+
// opérateur de lecture, code exécuté en accédant à obj.propName
17+
},
18+
19+
*!*set propName(value)*/!* {
20+
// opérateur d'écriture, code exécuté en assignant obj.propName = value
21+
}
22+
};
23+
```
24+
25+
L'opérateur de lecture fonctionne lors de la lecture de `obj.propName`, l'opérateur d'écriture -- lors de son affectation.
26+
27+
Par exemple, nous avons un objet `user` avec `name` et `surname`:
28+
29+
```js
30+
let user = {
31+
name: "John",
32+
surname: "Smith"
33+
};
34+
```
35+
36+
Maintenant nous voulons ajouter une propriété `fullName`, qui serait`"John Smith"`. Bien sûr, nous ne voulons pas copier-coller l'information existante, aussi nous pouvons la créer comme accesseur:
37+
38+
```js run
39+
let user = {
40+
name: "John",
41+
surname: "Smith",
42+
43+
*!*
44+
get fullName() {
45+
return `${this.name} ${this.surname}`;
46+
}
47+
*/!*
48+
};
49+
50+
*!*
51+
alert(user.fullName); // John Smith
52+
*/!*
53+
```
54+
55+
De l'extérieur, une propriété d'accesseur ressemble à une propriété classique. C'est l'idée avec les propriétés des accesseurs. Nous n'utilisons pas *call* pour appeler `user.fullName` comme une fonction, nous la lisons *read* normalement: l'accesseur s'exécute en coulisses.
56+
57+
A partir de maintenant, `fullName` a juste un accesseur. Si nous tentons d'affecter `user.fullName=`, cela provoquera une erreur:
58+
59+
```js run
60+
let user = {
61+
get fullName() {
62+
return `...`;
63+
}
64+
};
65+
66+
*!*
67+
user.fullName = "Test"; // Error (property has only a getter)
68+
*/!*
69+
```
70+
71+
Corrigeons en ajoutant un opérateur d'écriture pour `user.fullName`:
72+
73+
```js run
74+
let user = {
75+
name: "John",
76+
surname: "Smith",
77+
78+
get fullName() {
79+
return `${this.name} ${this.surname}`;
80+
},
81+
82+
*!*
83+
set fullName(value) {
84+
[this.name, this.surname] = value.split(" ");
85+
}
86+
*/!*
87+
};
88+
89+
// set fullName est exécuté avec la valeur donnée.
90+
user.fullName = "Alice Cooper";
91+
92+
alert(user.name); // Alice
93+
alert(user.surname); // Cooper
94+
```
95+
96+
Le résultat obtenu est une propriété "virtuelle" `fullName`. Elle est lisible et inscriptible.
97+
98+
## Descripteurs de l'accesseur
99+
100+
Les descripteurs pour les propriétés de l'accesseur sont différents de ceux concernant les propriétés de données.
101+
102+
Pour les propriétés de l'accesseur, il n'y a pas de valeur `value` ou inscriptible `writable`, mais à la place il y a les fonctions `get` et `set`.
103+
104+
Ainsi, un descripteur d'accesseur peut avoir:
105+
106+
- **`get`** -- une fonction sans arguments, qui s'exécute quand une propriété est lue,
107+
- **`set`** -- une fonction avec un argument, qui est appelée quand la propriété est écrite,
108+
- **`enumerable`** -- identique aux propriétés de données,
109+
- **`configurable`** -- identique aux propriétés de données.
110+
111+
Par exemple, pour créer un accesseur `fullName` avec `defineProperty`, nous pouvons passer un descripteur avec `get` et `set`:
112+
113+
```js run
114+
let user = {
115+
name: "John",
116+
surname: "Smith"
117+
};
118+
119+
*!*
120+
Object.defineProperty(user, 'fullName', {
121+
get() {
122+
return `${this.name} ${this.surname}`;
123+
},
124+
125+
set(value) {
126+
[this.name, this.surname] = value.split(" ");
127+
}
128+
*/!*
129+
});
130+
131+
alert(user.fullName); // John Smith
132+
133+
for(let key in user) alert(key); // prénom, nom
134+
```
135+
136+
Veuillez noter qu'une propriété peut être soit un accesseur (a les méthodes `get/set`) soit une propriété de donnée (a une valeur `value`), pas les deux ensemble.
137+
138+
Si nous essayons d'utiliser à la fois `get` et `value` dans le même descripteur, cela produira une erreur:
139+
140+
```js run
141+
*!*
142+
// Error: Invalid property descriptor.
143+
*/!*
144+
Object.defineProperty({}, 'prop', {
145+
get() {
146+
return 1
147+
},
148+
149+
value: 2
150+
});
151+
```
152+
153+
## Des opérateurs de lecture/écriture plus intelligents
154+
155+
Les opérateurs de lecture/écriture peuvent être utilisés comme des fonctions wrapper (pour appeler une ou d'autres fonctions) à la place de valeurs "réelles" d'une propriété en vue d'augmenter le contrôle des opérations.
156+
157+
Par exemple, si nous voulons interdire les noms trop courts pour `user`, nous pouvons avoir un opérateur d'écriture `name` et garder la valeur dans une propriété séparée `_name`:
158+
159+
```js run
160+
let user = {
161+
get name() {
162+
return this._name;
163+
},
164+
165+
set name(value) {
166+
if (value.length < 4) {
167+
alert("Name is too short, need at least 4 characters");
168+
return;
169+
}
170+
this._name = value;
171+
}
172+
};
173+
174+
user.name = "Pete";
175+
alert(user.name); // Pete
176+
177+
user.name = ""; // Le nom est trop court...
178+
```
179+
180+
Ainsi, le nom est stocké dans la propriété `_name`, et l'accès s'effectue via les opérateurs de lecture et d'écriture.
181+
182+
Techniquement, le code externe est capable d'accéder au nom directement en utilisant `user._name`. Mais d'après une convention largement reconnue, les propriétés commençant avec un underscore `"_"` sont internes et ne devraient pas être accessibles hors de l'objet.
183+
184+
185+
## Utilisation pour la compatibilité
186+
187+
Une des utilisations géniales des accesseurs est qu'ils permettent de prendre le contrôle d'une propriété de données "classique" à tout moment en la remplaçant par un opérateur de lecture et d'écriture, et de modifier son comportement.
188+
189+
Imaginez que nous commencions à mettre en oeuvre des objets utilisateurs utilisant les propriétés de données `name` et `age`:
190+
191+
```js
192+
function User(name, age) {
193+
this.name = name;
194+
this.age = age;
195+
}
196+
197+
let john = new User("John", 25);
198+
199+
alert( john.age ); // 25
200+
```
201+
202+
...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 commode:
203+
204+
```js
205+
function User(name, birthday) {
206+
this.name = name;
207+
this.birthday = birthday;
208+
}
209+
210+
let john = new User("John", new Date(1992, 6, 1));
211+
```
212+
213+
Maintenant, que faire avec l'ancien code qui utilise encore la propriété `age`?
214+
215+
Nous pouvons essayer de trouver toutes les occurences et les corriger mais cela prend du temps et peut être compliqué si ce code est utilisé par beaucoup d'autres personnes. Et en plus, `age` est une bonne chose à avoir dans `user`, n'est-ce pas?
216+
217+
Gardons-le.
218+
219+
L'ajout d'un opérateur de lecture pour `age` résoud le problème:
220+
221+
```js run no-beautify
222+
function User(name, birthday) {
223+
this.name = name;
224+
this.birthday = birthday;
225+
226+
*!*
227+
// l'âge est calculé à partir de la date actuelle et de l'anniversaire
228+
Object.defineProperty(this, "age", {
229+
get() {
230+
let todayYear = new Date().getFullYear();
231+
return todayYear - this.birthday.getFullYear();
232+
}
233+
});
234+
*/!*
235+
}
236+
237+
let john = new User("John", new Date(1992, 6, 1));
238+
239+
alert( john.birthday ); // anniversaire est disponible
240+
alert( john.age ); // ...ainsi que l'âge
241+
```
242+
243+
Maintenant l'ancien code fonctionne aussi et nous avons une chouette propriété supplémentaire.
244+

0 commit comments

Comments
 (0)