Una breve introduzione alla metaprogrammazione in JavaScript
Reflection
Reflection, unliked “code generation”, è un processo per cambiare la meccanica sottostante del linguaggio. La riflessione può avvenire in fase di compilazione o in fase di runtime, ma rimarremo con la riflessione di runtime mentre stiamo parlando di JavaScript, quindi la riflessione in fase di compilazione non sarà possibile. Tuttavia, i concetti discussi qui potrebbero essere applicabili anche a un linguaggio compilato.
Poiché abbiamo capito che la riflessione riguarda il cambiamento della meccanica sottostante del linguaggio, è stata divisa in tre categorie principali, vale a dire. introspezione, intercessione e modifica.
Introspezione
L’introspezione è il processo di analisi del programma. Se puoi dire quale programma fa, puoi modificarlo secondo i tuoi gusti. Anche se alcuni linguaggi di programmazione non supportano la generazione di codice o le funzionalità di modifica del codice, ma molto probabilmente consentono l’introspezione.
Un semplice esempio di introspezione potrebbe essere l’utilizzo di typeof
o instanceof
operatori in JavaScript. typeof
restituisce il tipo di dati di un valore o un’espressione che restituisce un valore), mentre instanceof
torna true
o false
se LHS valore è un’istanza di RHS classe. Vediamoli in azione.
il programma di cui sopra, abbiamo usato typeof
e instanceof
operatori coerce
funzione di annusare il tipo di dati in ingresso value
. Questa è la dimostrazione di base dell’introspezione. Tuttavia, un linguaggio specificamente progettato per la metaprogrammazione potrebbe fornire alcuni potenti strumenti di introspezione.
È possibile utilizzare l’operatore in
per verificare se esiste una proprietà nell’oggetto. La funzione globaleisNaN
controlla se l’oggetto èNaN
. Ci sono alcuni metodi statici costruito intorno al Object
per ispezionare i valori di Object
tipo Object.isFrozen(value)
per verificare se value
è congelato o Object.keys(value)
per ottenere i nomi di proprietà del value
oggetto.
Fino a ES5, avevamo questi operatori e questi metodi con cui lavorare. In ES2015 (ES6), JavaScript ha introdotto Reflect
oggetto che fornisce alcuni metodi statici (proprio come Object
) ma specificamente progettato per l’introspezione. Poiché abbiamo una lezione separata su Reflect
, questi metodi sono discussi lì.
Intercessione
Intercessione è il processo di intervenire nei processi JavaScript e modificare il comportamento standard del processo. JavaScript fornisce ottimi strumenti per l’intercessione uno dei quali è Proxy
.
La classe Proxy
è stata introdotta in ES2015 (ES6) per intercettare (intervenire) operazioni JavaScript di base attorno agli oggetti proprio come abbiamo visto sopra, ma in un modo molto più bello. Abbiamo una lezione separata suProxy
(in arrivo) ma in poche parole,Proxy
avvolge una logica intercettabile attorno a un oggetto.
var targetWithProxy = new Proxy(target, handler);
Qui, iltarget
è oggetto ehandler
è l’intercettore. handler
è anche un semplice oggetto JavaScript ma con alcuni campi significativi. For example, handler.get
would be a function that returns a custom value when target.prop
(here, prop
is any property) is accessed.
Proxy is a great way to provide abstractions over your not-so-public data. Ad esempio, nel programma di cui sopra, abbiamo fornito astrazioni su target
oggetto e personalizzato come dovrebbe presentarsi al pubblico.
Qualche intercessione era possibile anche in ES5 come usare getter
e setters
sui descrittori di proprietà, ma avrebbe comportato la mutazione dell’oggetto target
Proxy
fornisce un modo molto più pulito per ottenere l’intercessione senza dover modificare l’oggetto originale (target
).
Modifica
La modifica si riferisce alla modifica del comportamento del programma attraverso la mutazione. Nel caso di intercessione, abbiamo intercettato solo i processi JavaScript standard aggiungendo una logica di intercettazione tra il bersaglio e il ricevitore senza danneggiare il bersaglio. In questo caso modifica, stiamo cambiando il comportamento del bersaglio stesso in modo da soddisfare il ricevitore.
Sovrascrivere un’implementazione di una funzione sarebbe un buon esempio di modifica. Ad esempio, se una funzione è progettata per comportarsi in un certo modo, ma vogliamo qualcos’altro condizionatamente, possiamo farlo progettando una funzione di auto-sovrascrittura. Vediamo un esempio.
Nell’esempio precedente, abbiamo creato una funzione che si sostituisce con una nuova implementazione della funzione. Questo sarebbe l’esempio più duro di modifica, ma abbiamo altri casi d’uso forse più significativi.
Nell’esempio precedente, abbiamo usato il metodoObject.defineProperty()
per modificare il descrittore di proprietà predefinito della proprietàname
per renderlo di sola lettura. È inoltre possibile utilizzare ilObject.freeze()
metodo per bloccare l’intero oggetto per evitare eventuali mutazioni.
Alcune intercessioni possono avvenire attraverso modifiche come è possibile dall’esempio precedente. Impostando writable:false
nel descrittore di proprietà dell’oggetto, quindi mutando l’oggetto (implementazione interna), abbiamo iniziato l’operazione di assegnazione del valore.
Se non si ha familiarità con il metodo valueOf
, viene utilizzato per costringere un oggetto a un valore primitivo. Quindi se ho un oggetto e ha un metodovalueOf
su se stesso o sulla sua catena di prototipi, allora questo metodo viene chiamato da JavaScript quando si sta tentando di eseguire un’operazione aritmetica su di esso. Per impostazione predefinita, Object
ha ilvalueOf
metodo che restituisce se stesso (l’oggetto).
Come puoi vedere nell’esempio precedente,emp1/10
ha prodotto unNaN
poiché un oggetto non può essere diviso come numeri naturali. Ma in seguito abbiamo aggiuntovalueOf
metodo su Employee
classe che restituisce salary
valore dell’oggetto. Pertantoemp2/10
restituito200
poichéemp2.salary
200
. Allo stesso modo,emp3/10
restituito300
come abbiamo aggiuntovalueOf
metodo direttamente sulemp3
.
Quindi ad ogni passo dell’esempio precedente, stiamo intervenendo su come un oggetto viene presentato a un’operazione JavaScript standard e cambiando il suo comportamento in base ai nostri gusti. Questa non è altro che l’intercessione.
In ES2015 (ES6), JavaScript ha introdotto un nuovo tipo di dati primitivo che èsymbol
. Non è niente come abbiamo visto prima e non può essere rappresentato in forma letterale. Può essere costruito solo chiamando la funzioneSymbol
.
var sym1 = Symbol();
var sym2 = Symbol();
var sym3 = Symbol('description'); // description for debugging aidsym1 === sym2 // false
sym1 === sym2 // falsetypeof sym1 // 'symbol'console.log( sym1 ); // 'Symbol()'
console.log( sym3 ); // 'Symbol(description)'
In poche parole, produce valori univoci che possono essere utilizzati anche come chiavi oggetto regolari utilizzando la notazione obj
dove key
sarebbe un simbolo.
var key = Symbol();var obj = {
name: 'Ross',
: 200
};console.log( obj.name ); // 'Ross'
console.log( obj ); // 200
Object.keys(obj); // obj = 300;
Poiché sono unici, non c’è modo di creare un simbolo duplicato per caso. Ogni nuovo simbolo è unico (creato usando Symbol()
) il che significa che se si desidera utilizzare uno stesso simbolo, è necessario memorizzarlo in una variabile e passare quella variabile per fare riferimento allo stesso simbolo.
Nell’esempio valueOf
, è possibile individuare il problema se non siamo attenti o consapevoli. Poiché valueOf
è una proprietà string
(come in emp3
) , chiunque può sovrascriverlo accidentalmente o qualcuno che non conosce valueOf
potrebbe pianificare di usarlo per il proprio uso pensando “cosa c’è di nel nome?”.
Poiché i simboli possono anche essere usati come chiavi oggetto, JavaScript ha fornito alcuni simboli globali che dovrebbero essere usati come chiavi oggetto per alcune operazioni JavaScript standard. Poiché questi simboli sono ben noti a uno sviluppatore, sono chiamati “simboli ben noti”. Questi simboli ben noti sono esposti al pubblico come proprietà statiche della funzioneSymbol
.
Uno dei simboli ben noti è Symbol.toPrimitive
che dovrebbe essere usato come chiave dell’oggetto per ottenere il suo valore primitivo. Sì, stai pensando bene, è una sostituzione del metodo valueOf
ed è preferito.
The Il metodo
toPrimitive
non si limita a restituire un valore numerico dell’oggetto. Si prega di leggere i simboli lezioni per saperne di più su di esso.
JavaScript fornisce molti di questi simboli ben noti per intercettare e modificare il comportamento JavaScript predefinito intorno agli oggetti. Parleremo di questo e dei simboli in generale nella lezione dei simboli.