Avvolgere le funzioni JavaScript ti permette di aggiungere la logica comune a funzioni che non controlli, come le funzioni native ed esterne. Molte librerie JavaScript, come gli agenti TrackJS, hanno bisogno di avvolgere funzioni esterne per fare il loro lavoro. L’aggiunta di wrapper ci permette di ascoltare la telemetria, gli errori e i log nel vostro codice, senza che voi dobbiate chiamare esplicitamente le nostre API.
Potreste voler avvolgere una funzione per aggiungere una strumentazione o una logica di debug temporanea. Puoi anche cambiare il comportamento di una libreria esterna senza bisogno di modificare il sorgente.
Basic Function Wrapping
Perché JavaScript è meravigliosamente dinamico, possiamo wrappare una funzione semplicemente ridefinendo la funzione con qualcosa di nuovo. Per esempio, considerate questo wrapping di myFunction
:
In questo esempio banale, abbiamo avvolto l’originale myFunction
e aggiunto un messaggio di log. Ma ci sono molte cose che non abbiamo gestito:
- Come passiamo gli argomenti della funzione?
- Come manteniamo lo scope (il valore di
this
)? - Come otteniamo il valore di ritorno?
- Cosa succede se succede un errore?
Per gestire queste cose, dobbiamo diventare un po’ più intelligenti nel nostro wrapping.
Nota che non stiamo solo invocando la funzione in questo esempio, ma call
-ingrandola con il valore di this
e gli argomenti a
, b
e c
. Il valore di this
verrà passato da qualsiasi punto in cui collegate la vostra funzione wrapped, Window
in questo esempio.
Abbiamo anche circondato l’intera funzione in un blocco try/catch
in modo da poter invocare una logica personalizzata in caso di errore, rilanciarla, o restituire un valore predefinito.
Avanzate il wrapping delle funzioni
L’esempio di wrapping di base funzionerà il 90% delle volte, ma se state costruendo librerie condivise, come gli agenti TrackJS, questo non è abbastanza! Per avvolgere le nostre funzioni come un professionista, ci sono alcuni casi limite che dovremmo affrontare:
- Che dire degli argomenti non dichiarati o sconosciuti?
- Come facciamo a far corrispondere la firma della funzione?
- Come rispecchiamo le proprietà della funzione?
Ci sono 3 cambiamenti sottili ma importanti. Per prima cosa (#1), abbiamo dato un nome alla funzione. Sembra ridondante, ma il codice utente può controllare il valore di function.name
, quindi è importante mantenere il nome durante il wrapping.
Il secondo cambiamento (#2) è in come abbiamo chiamato la funzione wrapped, usando apply
invece di call
. Questo ci permette di passare un oggetto arguments
, che è un oggetto simile a un array di tutti gli argomenti passati alla funzione in fase di esecuzione. Questo ci permette di supportare funzioni che possono avere un numero indefinito o variabile di argomenti.
Con apply
, non abbiamo bisogno degli argomenti a
, b
e c
definiti nella firma della funzione. Ma continuando a dichiarare gli stessi argomenti della funzione originale, manteniamo l’arità della funzione. Cioè, Function.length
restituisce il numero di argomenti definiti nella firma, e questo rispecchierà la funzione originale.
La modifica finale (#3) copia qualsiasi proprietà specificata dall’utente dalla funzione originale sul nostro wrapping.
Limitazioni
Questo wrapping è completo, ma ci sono sempre limitazioni in JavaScript. In particolare, è difficile avvolgere correttamente una funzione con un prototipo non standard, come un costruttore di oggetti. Questo è un caso d’uso meglio risolto dall’ereditarietà.
In generale, cambiare il prototipo di una funzione è possibile, ma non è una buona idea. Ci sono serie implicazioni di performance ed effetti collaterali non voluti nella manipolazione dei prototipi.
Rispetta l’ambiente
Il wrapping delle funzioni ti dà molto potere per strumentare e manipolare l’ambiente JavaScript. Avete la responsabilità di esercitare questo potere con saggezza. Se state costruendo dei wrapper di funzioni, assicuratevi di rispettare l’utente e l’ambiente in cui state operando. Ci possono essere altri wrapper in atto, altri ascoltatori collegati per gli eventi, e aspettative sulle API delle funzioni. Andateci piano e non rompete il codice esterno.