You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/06-advanced-functions/09-call-apply-decorators/article.md
+35-36Lines changed: 35 additions & 36 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,7 +10,7 @@ Si la fonction est appelée souvent, nous voudrons peut-être mettre en mémoire
10
10
11
11
Mais au lieu d’ajouter cette fonctionnalité à `slow()`, nous allons créer une fonction wrapper qui ajoute la mise en cache. Comme nous le verrons, cela présente de nombreux avantages.
12
12
13
-
Voici le code, et les explications suivent:
13
+
Voici le code, et les explications suivent:
14
14
15
15
```js run
16
16
functionslow(x) {
@@ -29,7 +29,7 @@ function cachingDecorator(func) {
29
29
30
30
let result =func(x); // sinon appeler func
31
31
32
-
cache.set(x, result); // et cache (se souvenir) le résultat
32
+
cache.set(x, result); // et mettre le résultat en cache
33
33
return result;
34
34
};
35
35
}
@@ -43,30 +43,29 @@ alert( slow(2) ); // slow(2) est mis en cache et le résultat est renvoyé
43
43
alert( "Again: "+slow(2) ); // le résultat slow(2) est retourné à partir du cache
44
44
```
45
45
46
-
Dans le code ci-dessus, `cachingDecorator` est un *décorateur*: une fonction spéciale qui prend une autre fonction et modifie son comportement.
46
+
Dans le code ci-dessus, `cachingDecorator` est un *décorateur*: une fonction spéciale qui prend une autre fonction et modifie son comportement.
47
47
48
48
L'idée est que nous pouvons appeler `cachingDecorator` pour n'importe quelle fonction, ce qui renverra le wrapper de mise en cache. C'est formidable, car nous pouvons avoir de nombreuses fonctions qui pourraient utiliser une telle fonctionnalité, et tout ce que nous avons à faire est de leur appliquer `cachingDecorator`.
49
49
50
50
En séparant la mise en cache du code de la fonction principale, nous simplifions également le code principal.
51
51
52
-
Le résultat de `cachingDecorator(func)` est un "wrapper": `function(x)` qui "encapsule" l'appel de `func(x)` dans la logique de mise en cache:
52
+
Le résultat de `cachingDecorator(func)` est un "wrapper": `function(x)` qui "encapsule" l'appel de `func(x)` dans la logique de mise en cache:
53
53
54
54

55
55
56
56
Depuis un code extérieur, la fonction encapsulée `slow` fait toujours la même chose. Un comportement de mise en cache vient d’être ajouté à son comportement.
57
57
58
-
Pour résumer, il y a plusieurs avantages à utiliser un `cachingDecorator` distinct au lieu de modifier le code de `slow` lui-même:
58
+
Pour résumer, il y a plusieurs avantages à utiliser un `cachingDecorator` distinct au lieu de modifier le code de `slow` lui-même:
59
59
60
60
- Le `cachingDecorator` est réutilisable. Nous pouvons l'appliquer à une autre fonction.
61
-
- La logique de mise en cache est séparée, elle n’a pas augmenté la complexité de `slow` lui-même (s’il en existait)
61
+
- La logique de mise en cache est séparée, elle n’a pas augmenté la complexité de `slow` lui-même (s’il en existait).
62
62
- Nous pouvons combiner plusieurs décorateurs si nécessaire (d'autres décorateurs suivront).
63
63
64
-
65
64
## Utilisation de "func.call" pour le contexte
66
65
67
66
Le décorateur de mise en cache mentionné ci-dessus n'est pas adapté pour travailler avec des méthodes d'objet.
68
67
69
-
Par exemple, dans le code ci-dessous `worker.slow()` cesse de fonctionner après la décoration:
68
+
Par exemple, dans le code ci-dessous `worker.slow()` cesse de fonctionner après la décoration:
70
69
71
70
```js run
72
71
// on ajoutera une fonctionalité de cache à worker.slow
L'erreur se produit dans la ligne `(*)` qui tente d'accéder à `this.someMethod` et échoue. Pouvez-vous voir pourquoi?
108
+
L'erreur se produit dans la ligne `(*)` qui tente d'accéder à `this.someMethod` et échoue. Pouvez-vous voir pourquoi?
110
109
111
110
La raison en est que le wrapper appelle la fonction d'origine sous la forme `func(x)` dans la ligne `(**)`. Et, lorsqu'elle est appelée comme ça, la fonction obtient `this = undefined`.
112
111
113
-
Nous observerions un symptôme similaire si nous essayions d'executer:
112
+
Nous observerions un symptôme similaire si nous essayions d'executer:
114
113
115
114
```js
116
115
let func =worker.slow;
@@ -123,23 +122,24 @@ Réparons-le.
123
122
124
123
Il existe une méthode de fonction intégrée spéciale [func.call(context, ...args)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/call) qui permet d'appeler explicitement une fonction en définissant `this`.
125
124
126
-
La syntaxe est la suivante:
125
+
La syntaxe est la suivante:
127
126
128
127
```js
129
128
func.call(context, arg1, arg2, ...)
130
129
```
131
130
132
131
Il exécute `func` en fournissant `this` comme le premier argument et les suivants en tant qu'arguments.
133
132
134
-
Pour le dire simplement, ces deux appels font presque la même chose:
133
+
Pour le dire simplement, ces deux appels font presque la même chose :
134
+
135
135
```js
136
136
func(1, 2, 3);
137
137
func.call(obj, 1, 2, 3)
138
138
```
139
139
140
140
Ils appellent tous les deux `func` avec les arguments `1`, `2` et `3`. La seule différence est que `func.call` définit également `this` sur `obj`.
141
141
142
-
Par exemple, dans le code ci-dessous, nous appelons `sayHi` dans le contexte de différents objets: `sayHi.call(user)` exécute `sayHi` fournissant `this = user`, et la ligne suivante définit `this = admin`:
142
+
Par exemple, dans le code ci-dessous, nous appelons `sayHi` dans le contexte de différents objets: `sayHi.call(user)` exécute `sayHi` fournissant `this = user`, et la ligne suivante définit `this = admin`:
143
143
144
144
```js run
145
145
functionsayHi() {
@@ -154,8 +154,7 @@ sayHi.call( user ); // John
154
154
sayHi.call( admin ); // Admin
155
155
```
156
156
157
-
Et ici, nous utilisons `call` pour appeler `say` avec le contexte et la phrase donnés:
158
-
157
+
Et ici, nous utilisons `call` pour appeler `say` avec le contexte et la phrase donnés :
159
158
160
159
```js run
161
160
functionsay(phrase) {
@@ -204,22 +203,22 @@ alert( worker.slow(2) ); // ça fonctionne, n'appelle pas l'original (mis en cac
204
203
205
204
Maintenant, tout va bien.
206
205
207
-
Pour que tout soit clair, voyons plus en détail comment `this` est passé:
206
+
Pour que tout soit clair, voyons plus en détail comment `this` est passé:
208
207
209
208
1. Après la décoration, `worker.slow` est désormais le wrapper `function(x) {...}`.
210
209
2. Ainsi, lorsque `worker.slow(2)` est exécuté, le wrapper obtient `2` en argument et `this = worker` (c'est l'objet avant le point).
211
-
3. Dans le wrapper, en supposant que le résultat ne soit pas encore mis en cache, `func.call(this, x)` passe le `this` (`= worker`) actuel et l'argument actuel (`= 2`) à la méthode d'origine. .
210
+
3. Dans le wrapper, en supposant que le résultat ne soit pas encore mis en cache, `func.call(this, x)` passe le `this` (`= worker`) actuel et l'argument actuel (`= 2`) à la méthode d'origine.
212
211
213
-
## Passer plusieurs arguments
212
+
## Passer plusieurs arguments
214
213
215
214
Rendons maintenant `cachingDecorator` encore plus universel. Jusqu'à présent, il ne travaillait qu'avec des fonctions à un seul argument.
216
215
217
-
Maintenant, comment mettre en cache la méthode multi-argument `worker.slow`?
216
+
Maintenant, comment mettre en cache la méthode multi-argument `worker.slow`?
Auparavant, pour un seul argument, `x`, nous pouvions simplement `cache.set(x, result)` pour enregistrer le résultat et `cache.get(x)` pour le récupérer. Mais maintenant, nous devons nous rappeler le résultat pour une *combinaison d'arguments*`(min, max)`. Le `Map` natif prend une valeur unique en tant que clé.
231
230
232
-
Il y a beaucoup de solutions possibles:
231
+
Il y a beaucoup de solutions possibles:
233
232
234
233
1. Mettre en œuvre une nouvelle structure de données similaire à `Map` (ou utiliser une par une tierce partie) plus polyvalent et permetant l'utilisation de plusieurs clés.
235
-
2. Utilisez des maps imbriquées: `cache.set(min)` sera un `Map` qui stocke la paire `(max, result)`. Donc, nous pouvons obtenir `result` avec `cache.get (min).get(max)`.
234
+
2. Utilisez des maps imbriquées: `cache.set(min)` sera un `Map` qui stocke la paire `(max, result)`. Donc, nous pouvons obtenir `result` avec `cache.get (min).get(max)`.
236
235
3. Joignez deux valeurs en une. Dans notre cas particulier, nous pouvons simplement utiliser la chaîne `"min, max"` comme clé pour `Map`. Pour plus de flexibilité, nous pouvons permettre de fournir une *fonction de hachage* au décorateur, qui sait créer une valeur parmi plusieurs.
237
236
238
237
Pour de nombreuses applications pratiques, la 3ème variante est suffisante, nous allons donc nous y tenir.
239
238
240
-
Nous devons également transmettre non seulement `x`, mais tous les arguments dans `func.call`. Rappelons que dans une `function()` on peut obtenir un pseudo-tableau de ses arguments comme `arguments`, donc`func.call(this, x)`doit être remplacé par `func.call(this, ...arguments)`.
239
+
Nous devons également transmettre non seulement `x`, mais tous les arguments dans `func.call`. Rappelons que dans une `function()` on peut obtenir un pseudo-tableau de ses arguments comme `arguments`, donc`func.call(this, x)`doit être remplacé par `func.call(this, ...arguments)`.
241
240
242
-
Voici un plus puissant `cachingDecorator` :
241
+
Voici un `cachingDecorator` plus puissant :
243
242
244
243
```js run
245
244
let worker = {
@@ -280,7 +279,7 @@ alert( "Again " + worker.slow(3, 5) ); // pareil (mis en cache)
280
279
281
280
Maintenant, cela fonctionne avec n'importe quel nombre d'arguments (bien que la fonction de hachage doive également être ajustée pour permettre n'importe quel nombre d'arguments. Une façon intéressante de gérer cela sera traitée ci-dessous).
282
281
283
-
Il y a deux changements:
282
+
Il y a deux changements:
284
283
285
284
- Dans la ligne `(*)`, il appelle `hash` pour créer une clé unique à partir de `arguments`. Ici, nous utilisons une simple fonction "d'assemblage" qui transforme les arguments `(3, 5)` en la clé `"3,5"`. Les cas plus complexes peuvent nécessiter d'autres fonctions de hachage.
286
285
- Ensuite `(**)` utilise `func.call(this, ...arguments)` pour transmettre le contexte et tous les arguments obtenus par le wrapper (pas seulement le premier) à la fonction d'origine.
@@ -329,7 +328,7 @@ When an external code calls such `wrapper`, it is indistinguishable from the cal
329
328
330
329
## Emprunter une méthode [#method-borrowing]
331
330
332
-
Maintenant, apportons une autre amélioration mineure à la fonction de hachage:
331
+
Maintenant, apportons une autre amélioration mineure à la fonction de hachage:
333
332
334
333
```js
335
334
functionhash(args) {
@@ -339,7 +338,7 @@ function hash(args) {
339
338
340
339
Pour l'instant, cela ne fonctionne que sur deux arguments. Ce serait mieux s'il pouvait coller un nombre quelconque de `args`.
341
340
342
-
La solution naturelle serait d'utiliser la méthode [arr.join](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/join):
341
+
La solution naturelle serait d'utiliser la méthode [arr.join](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/join):
343
342
344
343
```js
345
344
functionhash(args) {
@@ -349,7 +348,7 @@ function hash(args) {
349
348
350
349
... Malheureusement, ça ne marchera pas. Parce que nous appelons `hash(arguments)` et l’objet `arguments` est à la fois itérable et semblable à un tableau, mais pas un vrai tableau.
351
350
352
-
Donc, appeler `join` échouerait, comme on peut le voir ci-dessous:
351
+
Donc, appeler `join` échouerait, comme on peut le voir ci-dessous:
353
352
354
353
```js run
355
354
functionhash() {
@@ -361,7 +360,7 @@ function hash() {
361
360
hash(1, 2);
362
361
```
363
362
364
-
Néanmoins, il existe un moyen simple d’utiliser `join`:
363
+
Néanmoins, il existe un moyen simple d’utiliser `join`:
Nous prenons (empruntons) la méthode `join` d'un tableau régulier (`[].join`) et utilisons `[].join.call` pour l'exécuter dans le contexte des `arguments`.
379
378
380
-
Pourquoi ça marche?
379
+
Pourquoi ça marche?
381
380
382
381
C'est parce que l'algorithme interne de la méthode native `arr.join(glue)` est très simple.
383
382
384
-
Tiré de la spécification presque "tel quel":
383
+
Tiré de la spécification presque "tel quel":
385
384
386
385
1. Soit `glue` le premier argument ou, s’il n’ya pas d’argument, une virgule `","`.
387
386
2. Soit `result` une chaîne de caractères vide.
@@ -391,11 +390,11 @@ Tiré de la spécification presque "tel quel":
391
390
6. ... Faites-le jusqu'à ce que `this.length` éléments soient collés.
392
391
7. Retournez `result`.
393
392
394
-
Donc, techniquement, cela prend `this` et associe `this[0]`, `this[1]`... etc. Il est intentionnellement écrit de manière à permettre à tout type de tableau `this` (pas une coïncidence, de nombreuses méthodes suivent cette pratique). C'est pourquoi cela fonctionne aussi avec `this = arguments`.
393
+
Donc, techniquement, cela prend `this` et associe `this[0]`, `this[1]`... etc. Il est intentionnellement écrit de manière à permettre à tout type de tableau `this` (ce n'est pas une coïncidence, de nombreuses méthodes suivent cette pratique). C'est pourquoi cela fonctionne aussi avec `this = arguments`.
395
394
396
395
## Décorateurs et propriétés fonctionnelles
397
396
398
-
Il est généralement prudent de remplacer une fonction ou une méthode par une fonction décorée, à une exception près. Si la fonction d'origine comportait des propriétés, telles que `func.calledCount` ou autre, la fonction décorée ne les fournira pas. Parce que c'est un emballage. Il faut donc faire attention si on les utilise.
397
+
Il est généralement prudent de remplacer une fonction ou une méthode par une fonction décorée, à une exception près. Si la fonction d'origine comportait des propriétés, telles que `func.calledCount` ou autre, la fonction décorée ne les fournira pas. Parce que c'est un wrapper. Il faut donc faire attention si on les utilise.
399
398
400
399
Dans l'exemple ci-dessus, si la fonction `slow` avait des propriétés, alors `cachingDecorator(slow)` est un wrapper sans elles.
401
400
@@ -407,14 +406,14 @@ Il existe un moyen de créer des décorateurs qui conservent l'accès aux propri
407
406
408
407
*Decorator* est un wrapper autour d'une fonction qui modifie son comportement. Le travail principal est toujours effectué par la fonction.
409
408
410
-
Les décorateurs peuvent être considérés comme des "caractéristiques" ou des "aspects" pouvant être ajoutés à une fonction. Nous pouvons en ajouter un ou en ajouter plusieurs. Et tout ça sans changer de code!
409
+
Les décorateurs peuvent être considérés comme des "caractéristiques" ou des "aspects" pouvant être ajoutés à une fonction. Nous pouvons en ajouter un ou en ajouter plusieurs. Et tout ça sans changer son code!
411
410
412
-
Pour implémenter `cachingDecorator`, nous avons étudié des méthodes:
411
+
Pour implémenter `cachingDecorator`, nous avons étudié les méthodes:
413
412
414
413
-[func.call(context, arg1, arg2...)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/call) -- appelle `func` avec un contexte et des arguments donnés.
415
414
-[func.apply(context, args)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/apply) -- appelle `func` en passant `context` comme `this` et `args` sous forme de tableau dans une liste d'arguments.
416
415
417
-
Le renvoi d'appel, *call forwarding*, est généralement effectué avec `apply`:
416
+
Le renvoi d'appel, *call forwarding*, est généralement effectué avec `apply`:
0 commit comments