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/99-js-misc/01-proxy/article.md
+29-29Lines changed: 29 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# Proxy e Reflect
2
2
3
-
Un oggetto `Proxy` racchiude un altro oggetto e ne intercetta le operazioni, come quelle di lettura/scrittura e molte altre, può eventualmente gestirle a modo suo, oppure, in maniera del tutto trasparente, lasciare che sia l'oggetto ad occuoparsene.
3
+
Un oggetto `Proxy` racchiude un altro oggetto e ne intercetta le operazioni, come quelle di lettura/scrittura e molte altre, può eventualmente gestirle a modo suo, oppure, in maniera del tutto trasparente, lasciare che sia l'oggetto ad occuparsene.
4
4
5
5
I proxy vengono utilizzati da molte librerie ed alcuni framework per browsers. Ne vedremo molte applicazioni pratiche in questo articolo.
6
6
@@ -13,9 +13,9 @@ let proxy = new Proxy(target, handler)
13
13
```
14
14
15
15
-`target` -- è l'oggetto da racchiudere, può essere qualsiasi cosa, anche funzioni.
16
-
-`handler` -- configurazione del proxy: un oggetto con "trappole", metodi che intercettano operazioni. Ad esempio una "trappola" `get` per la lettura di una proprietà di `target`, `set` per la scrittura di una prorietà di `target`, e così via.
16
+
-`handler` -- configurazione del proxy: un oggetto con "trappole", metodi che intercettano operazioni. Ad esempio una "trappola" `get` per la lettura di una proprietà di `target`, `set` per la scrittura di una proprietà di `target`, e così via.
17
17
18
-
Per le operazioni sul `proxy`, se c'è un "tappola" corrispondente in `handler`, allora questa verrà eseguita, e il proxy potrà gestirla, altrimenti l'operazione verrà eseguita su `target`.
18
+
Per le operazioni sul `proxy`, se c'è un "trappola" corrispondente in `handler`, allora questa verrà eseguita, e il proxy potrà gestirla, altrimenti l'operazione verrà eseguita su `target`.
19
19
Come primo esempio, creiamo un proxy senza "trappole":
20
20
21
21
```js run
@@ -46,11 +46,11 @@ Per attivare più funzionalità, aggiungiamo qualche "trappola".
46
46
47
47
Cosa possiamo intercettare?
48
48
49
-
Per molte operazioni sugli oggetti, esiste un cosidetto "metodo interno" nella specifiche JavaScript che ne descrive il funzionamento a basso livello. Ad esempio `[[Get]]`, il metodo interno per la lettura delle proprietà, e `[[Set]]`, il metodo interno per la scrittura delle proprietà, e così via. Questi metodi vengono utilizzati solamente nelle specifiche, non possiamo invocarli direttamente utilizzandone il nome.
49
+
Per molte operazioni sugli oggetti, esiste un così detto "metodo interno" nella specifiche JavaScript che ne descrive il funzionamento a basso livello. Ad esempio `[[Get]]`, il metodo interno per la lettura delle proprietà, e `[[Set]]`, il metodo interno per la scrittura delle proprietà, e così via. Questi metodi vengono utilizzati solamente nelle specifiche, non possiamo invocarli direttamente utilizzandone il nome.
50
50
51
-
Le trappole "proxy" intercettano le invocazioni di questi metodi. Queste vengono elencate nelle[specifiche Proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) e nella tabella sottostante.
51
+
Le trappole "proxy" intercettano le invocazioni di questi metodi. Queste vengono elencate nelle[specifiche Proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) e nella tabella sottostante.
52
52
53
-
Per ogni metodo interno, esiste una "trappola" in questa tabella: il nome del metodo che possiamo aggiungere al parametro `handler` del `new Proxy` per interecettare l'operazione:
53
+
Per ogni metodo interno, esiste una "trappola" in questa tabella: il nome del metodo che possiamo aggiungere al parametro `handler` del `new Proxy` per intercettare l'operazione:
54
54
55
55
| Metodo Interno | Handler | Innescato quando... |
@@ -81,7 +81,7 @@ Esistono anche altre invarianti, come:
81
81
82
82
Le "trappole" possono intercettare queste operazioni, ma devono seguire le regole viste.
83
83
84
-
Le invaraianti assicurano che le funzionalità di linguaggio si comportino in maniera corretta e consistente. La lista completa delle invarianti è disponibile [nelle specifiche](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). Probabilmente non le violerai, a meno ché tu non stia facendo qualcosa di strano.
84
+
Le invarianti assicurano che le funzionalità di linguaggio si comportino in maniera corretta e consistente. La lista completa delle invarianti è disponibile [nelle specifiche](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). Probabilmente non le violerai, a meno ché tu non stia facendo qualcosa di strano.
85
85
```
86
86
87
87
Vediamo come funzionano con esempi pratici.
@@ -102,7 +102,7 @@ Utilizziamo `get` per implementare i valore di default di un oggetto.
102
102
103
103
Costruiremo un array numerico che ritornerà `0` per valori inesistenti.
104
104
105
-
Soltiamente, quando si prova ad accedere ad una elemento non esistente di un array, si ottiene `undefined`, ma noi costruiremo un proxy di un array che ritorna `0` nel caso in cui la prorpietà non esistesse:
105
+
Solitamente, quando si prova ad accedere ad una elemento non esistente di un array, si ottiene `undefined`, ma noi costruiremo un proxy di un array che ritorna `0` nel caso in cui la proprietà non esistesse:
106
106
107
107
```js run
108
108
let numbers = [0, 1, 2];
@@ -193,7 +193,7 @@ La "trappola" `set` si innesca quando si accede in scrittura ad una proprietà.
193
193
-`value` -- il valore della proprietà,
194
194
-`receiver` -- similmente alla trappola `get`, ha importanza solamente per le proprietà di tipo setter.
195
195
196
-
La trappola `set` dovrebbe ritornare `true` se è stata imposta correttamete, `false` altrimenti (innescando `TypeError`).
196
+
La trappola `set` dovrebbe ritornare `true` se è stata imposta correttamente, `false` altrimenti (innescando `TypeError`).
197
197
198
198
Utilizziamola per validare un nuovo valore:
199
199
@@ -293,7 +293,7 @@ user = new Proxy(user, {
293
293
alert( Object.keys(user) ); // <empty>
294
294
```
295
295
296
-
Perché? La motivazione è semplice: `Object.keys` ritorna solamente le prorpietà con la flag `enumerable`. Per verificarlo, invoca il metodo interno `[[GetOwnProperty]]`su ogni proprietà per ottenere [i suoi descrittori](info:property-descriptors). E in questo caso, poiché non ci sono proprietà, i descrittori sono vuoti, non abbiamo alcuna flag `enumerable`, quindi questa verrà saltata.
296
+
Perché? La motivazione è semplice: `Object.keys` ritorna solamente le proprietà con la flag `enumerable`. Per verificarlo, invoca il metodo interno `[[GetOwnProperty]]`su ogni proprietà per ottenere [i suoi descrittori](info:property-descriptors). E in questo caso, poiché non ci sono proprietà, i descrittori sono vuoti, non abbiamo alcuna flag `enumerable`, quindi questa verrà saltata.
297
297
298
298
Per far sì che `Object.keys` ritorni una proprietà, è necessario che, o questa esiste nell'oggetto con la flag `enumerable`, oppure possiamo intercettare l'invocazione di `[[GetOwnProperty]]` (tramite la trappola `getOwnPropertyDescriptor`), e ritornare un descrittore con `enumerable: true`.
299
299
@@ -440,9 +440,9 @@ Un'invocazione di `user.checkPassword()` passerà al proxy `user` come `this` (l
440
440
441
441
Quindi leghiamo il contesto dei metodi dell'oggetto all'oggetto originale, `target`, alla riga `(*)`. Le future invocazioni utilizzeranno `target` come `this`, senza alcuna trappola.
442
442
443
-
Questa soluzione solitamenete funziona, ma non è ideale, poiché un metodo potrebbe passare l'oggetto senza proxy ovunque, e a quel punto faremmo un errore: dov'è l'oggetto originale, e dov'è quello con il proxy?
443
+
Questa soluzione solitamente funziona, ma non è ideale, poiché un metodo potrebbe passare l'oggetto senza proxy ovunque, e a quel punto faremmo un errore: dov'è l'oggetto originale, e dov'è quello con il proxy?
444
444
445
-
Oltretutto, un oggetto potrebbe essere racchiuso in più proxy (più proxy potrebbero aggiungere diverse funzionalità all'oggetto), e nel caso in cui passassimo un oggettto senza proxy ad un metodo, potremmo ottenere conseguenze inaspettate.
445
+
Oltretutto, un oggetto potrebbe essere racchiuso in più proxy (più proxy potrebbero aggiungere diverse funzionalità all'oggetto), e nel caso in cui passassimo un oggetto senza proxy ad un metodo, potremmo ottenere conseguenze inaspettate.
446
446
447
447
Quindi, un proxy del genere non dovrebbe essere utilizzato ovunque.
448
448
@@ -465,13 +465,13 @@ let range = {
465
465
};
466
466
```
467
467
468
-
Vorremmo usare l'operatore `in` per verificare che un numero appartenga al`range`.
468
+
Vorremmo usare l'operatore `in` per verificare che un numero appartenga al`range`.
469
469
470
470
La trappola `has` intercetta le invocazioni di `in`.
471
471
472
472
`has(target, property)`
473
473
474
-
-`target` -- è l'oggetto target, passanto come primo argomento in `new Proxy`,
474
+
-`target` -- è l'oggetto target, passato come primo argomento in `new Proxy`,
475
475
-`property` -- nome della proprietà
476
476
477
477
Qui vediamo la demo:
@@ -502,7 +502,7 @@ Semplice zucchero sintattico, vero? Molto semplice da implementare.
502
502
503
503
Possiamo costruire un proxy anche per funzioni.
504
504
505
-
La trappola `apply(target, thisArg, args)` gestisce l'invocazinone di un proxy come funzione:
505
+
La trappola `apply(target, thisArg, args)` gestisce l'invocazione di un proxy come funzione:
506
506
507
507
-`target` è l'oggetto target (le funzioni sono oggetti in JavaScript),
508
508
-`thisArg` è il valore di `this`.
@@ -512,7 +512,7 @@ Ad esempio, il decorator `delay(f, ms)`, che abbiamo sviluppato nell'articolo <i
512
512
513
513
In quell'articolo lo abbiamo fatto senza proxy. Un'invocazione di `delay(f, ms)` ritornava una funzione che inoltra le chiamate di `f` dopo `ms` millisecondi.
514
514
515
-
Qui vediamo la precendente implementazione, basata sulla funzione:
515
+
Qui vediamo la precedente implementazione, basata sulla funzione:
516
516
517
517
```js run
518
518
functiondelay(f, ms) {
@@ -558,9 +558,9 @@ alert(sayHi.length); // 0 (nella dichiarazione del wrapper, ci sono zero argomen
558
558
*/!*
559
559
```
560
560
561
-
Il `proxy` è molto più potente, poiché inoltra tutto all'oggeto target.
561
+
Il `proxy` è molto più potente, poiché inoltra tutto all'oggetto target.
562
562
563
-
Utiizziamo il `Proxy` piuttosto della funzione di wrapping:
563
+
Utilizziamo il `Proxy` piuttosto della funzione di wrapping:
-`Reflect.set` scrive una proprietà di un oggetto e ritorna `true` se quest avviene con succesos, `false` altrimenti.
656
+
-`Reflect.set` scrive una proprietà di un oggetto e ritorna `true` se quest avviene con successo, `false` altrimenti.
657
657
658
658
Questo è tutto, piuttosto semplice: se una trappola vuole inoltrare l'invocazione all'oggetto, è sufficiente invocare `Reflect.<method>` con gli stessi argomenti.
659
659
@@ -733,7 +733,7 @@ Il problema sta quindi nel proxy, alla riga `(*)`.
733
733
734
734
Per evitare questo, abbiamo bisogno di `receiver`, il terzo argomento della trappola `get`. Questo fa riferimento al `this` corretto, quello che deve essere passato al getter. Nel nostro caso `admin`.
735
735
736
-
Come possiamo passare il contensto per un getter? Per una funzione regolare potremmo usare `call/apply`, ma questo è un getter, non viene "invocato", ma vi si accede semplicemenete.
736
+
Come possiamo passare il contesto per un getter? Per una funzione regolare potremmo usare `call/apply`, ma questo è un getter, non viene "invocato", ma vi si accede semplicemente.
737
737
738
738
`Reflect.get` fa al caso nostro. Tutto funzionerà correttamente se ne facciamo uso.
739
739
@@ -783,7 +783,7 @@ Quindi, `return Reflect...` è un modo sicuro e banale per inoltrare le operazio
783
783
784
784
## Limitazioni del proxy
785
785
786
-
I proxy forniscono un modo unico per alterare o aggirare il comportamentoa basso livello degli oggetti esistenti. Non è comunque perfetto. Ha delle limitazioni.
786
+
I proxy forniscono un modo unico per alterare o aggirare il comportamento a basso livello degli oggetti esistenti. Non è comunque perfetto. Ha delle limitazioni.
787
787
788
788
### Oggetti integrati: slot interni
789
789
@@ -832,7 +832,7 @@ Ora funziona senza problemi, poiché la trappola `get` si lega alle proprietà d
832
832
A differenza dell'esempio precedente, il valore di `this` all'interno di `proxy.set(...)` non sarà `proxy`, ma piuttosto sarà l'oggetto originale `map`. Quindi quando l'implementazione interna di `set` proverà ad accedere allo slot interno `this.[[MapData]]`, l'operazione avverrà con successo.
833
833
834
834
```smart header="`Array` non possiede slot interni"
835
-
Un'eccezione degna di nota: l'oggetto integrato `Array` non utilizza slot interni. Questo per ragioni storiche, poiché esistono da moltop tempo.
835
+
Un'eccezione degna di nota: l'oggetto integrato `Array` non utilizza slot interni. Questo per ragioni storiche, poiché esistono da molto tempo.
836
836
837
837
Quindi non avremo nessun problema nel creare proxy per un array.
838
838
```
@@ -924,7 +924,7 @@ I proxy possono intercettare molti operatori, come `new` (con `construct`), `in`
924
924
925
925
Ma non esiste alcun modo per poter intercettare un test di uguaglianza stretta tra oggetti. Un oggetto è strettamente uguale solamente a se stesso, e a nient altro.
926
926
927
-
Quindi tutte le operazioni ed le classi integrate che verificano l'uguaglianza tra oggetti differenezieranno l'oggetto dal suo proxy. Non c'è alcun sistema di sostituzione "trasparente" in questo caso.
927
+
Quindi tutte le operazioni ed le classi integrate che verificano l'uguaglianza tra oggetti differenzieranno l'oggetto dal suo proxy. Non c'è alcun sistema di sostituzione "trasparente" in questo caso.
928
928
```
929
929
930
930
## Proxy revocabili
@@ -933,7 +933,7 @@ Un proxy *revocabile* è un proxy che può essere disabilitato.
933
933
934
934
Ipotizziamo di avere una risorsa, di cui vorremo poter bloccare gli accessi in qualsiasi momento.
935
935
936
-
Quello che possiamo fare è creare un proxy *revocabile*, senza alcuna trappola. Un proxy di questo tipo, inoltrerà tutte le operazioni all'oggetto originale, e possiamo disabialitarlo in ogni momento.
936
+
Quello che possiamo fare è creare un proxy *revocabile*, senza alcuna trappola. Un proxy di questo tipo, inoltrerà tutte le operazioni all'oggetto originale, e possiamo disabilitarlo in ogni momento.
937
937
938
938
La sintassi da utilizzare è la seguente:
939
939
@@ -968,7 +968,7 @@ Inizialmente, `revoke` è separato da `proxy`, in questo modo possiamo passare
968
968
969
969
Possiamo anche legare il metodo `revoke` al proxy, impostando `proxy.revoke = revoke`.
970
970
971
-
Un'altra opzione è quela di creare una `WeakMap` che possiede il `proxy` come chiave e il corrispondente `revoke` come valore, questo consente di trovare facilemente il `revoke` per un proxy:
971
+
Un'altra opzione è quella di creare una `WeakMap` che possiede il `proxy` come chiave e il corrispondente `revoke` come valore, questo consente di trovare facilmente il `revoke` per un proxy:
972
972
973
973
```js run
974
974
*!*
@@ -990,7 +990,7 @@ revoke();
990
990
alert(proxy.data); // Errore (revocato)
991
991
```
992
992
993
-
In questo casol, utilizziamo una `WeakMap` piuttosto di `Map` in modo che non blocchi il processo di garbage collection. Se un proxy diventa "irragiungibile" (e.g. nessuna variabile fa riferimento ad esso), `WeakMap` consente di rimuoverlo dalla memoria insieme al relativo `revoke` che non sarà più necessario.
993
+
In questo casol, utilizziamo una `WeakMap` piuttosto di `Map` in modo che non blocchi il processo di garbage collection. Se un proxy diventa "irraggiungibile" (e.g. nessuna variabile fa riferimento ad esso), `WeakMap` consente di rimuoverlo dalla memoria insieme al relativo `revoke` che non sarà più necessario.
994
994
995
995
## Riferimenti
996
996
@@ -1001,7 +1001,7 @@ In questo casol, utilizziamo una `WeakMap` piuttosto di `Map` in modo che non bl
1001
1001
1002
1002
Il `Proxy` è un contenitore per un oggetto, che inoltra tutte le operazioni su di esso all'oggetto originale, e consente di definire delle "trappole" per determinate operazioni.
1003
1003
1004
-
E' possile creare un proxy per qualsiasi tipo di oggetto, incluse le classi e le funzioni.
1004
+
E' possibile creare un proxy per qualsiasi tipo di oggetto, incluse le classi e le funzioni.
1005
1005
1006
1006
La sintassi da utilizzare è la seguente:
1007
1007
@@ -1019,7 +1019,7 @@ Possiamo intercettare:
1019
1019
- Operatore `new` (trappola `construct`).
1020
1020
- Molte altre operazioni (puoi trovare la lista completa a inizio articolo e nella [documentazione](mdn:/JavaScript/Reference/Global_Objects/Proxy)).
1021
1021
1022
-
Questo ci consente di creare proprietà e motodi "virtuali", implementare valori di default, oggetti observables, decorators e molto altro.
1022
+
Questo ci consente di creare proprietà e metodi "virtuali", implementare valori di default, oggetti observables, decorators e molto altro.
1023
1023
1024
1024
Possiamo anche costruire proxy multipli di un oggetto, decorandolo con divers e funzionalità.
1025
1025
@@ -1028,6 +1028,6 @@ L'API [Reflect](mdn:/JavaScript/Reference/Global_Objects/Reflect) è stata proge
1028
1028
I proxy hanno però delle limitazioni:
1029
1029
1030
1030
- Gli oggetti integrati possiedono degli "slot interni", ma l'accesso a questi non può essere intercettato dai proxy. Guardate il workaround descritto sopra.
1031
-
- Lo stesso vale per i campi privati della classe, questi vengono implementati internametne utilizzando gli slot. Quindi le invocazioni dei metodi tramite proxy, devono possedere il target object asseganto a `this` per potervi accedere.
1031
+
- Lo stesso vale per i campi privati della classe, questi vengono implementati internamente utilizzando gli slot. Quindi le invocazioni dei metodi tramite proxy, devono possedere il target object assegnato a `this` per potervi accedere.
1032
1032
- I test di uguaglianza `===` non possono essere intercettati.
1033
1033
- Performance: i benchmark dipendono molto dal motore JavaScript, ma generalmente l'accesso alle proprietà utilizzando un proxy, richiede più tempo. Anche se nella pratica, questo ha importanza solo per oggetti che creano "colli di bottiglia".
0 commit comments