Scopo degli esterni
Gli extern sono dichiarazioni che indicano a Closure Compiler i nomi dei simboli che non devono essere rinominati durante la compilazione avanzata. Sono chiamati extern perché questi simboli sono spesso definiti da codice esterno alla compilazione, ad esempio codice nativo o librerie di terze parti. Per questo motivo, spesso gli extern hanno anche annotazioni di tipo, in modo che Closure Compiler possa eseguire il controllo del tipo di utilizzo di questi simboli.
In generale, è meglio considerare gli extern come un contratto API tra l'implementatore e i consumer di una parte di codice compilato. Gli extern definiscono ciò che l'implementatore promette di fornire e ciò che i consumatori possono utilizzare. Entrambe le parti hanno bisogno di una copia del contratto.
Gli extern sono simili ai file di intestazione in altri linguaggi.
Sintassi di extern
Gli extern sono file molto simili al normale JavaScript annotato per Closure Compiler. La differenza principale è che i loro contenuti non vengono mai stampati come parte dell'output compilato, quindi nessuno dei valori è significativo, solo i nomi e i tipi.
Di seguito è riportato un esempio di file externs per una semplice libreria.
// The `@externs` annotation is the best way to indicate a file contains externs. /** * @fileoverview Public API of my_math.js. * @externs */ // Externs often declare global namespaces. const myMath = {}; // Externs can declare functions, most importantly their names. /** * @param {number} x * @param {number} y * @return {!myMath.DivResult} */ myMath.div = function(x, y) {}; // Note the empty body. // Externs can contain type declarations, such as classes and interfaces. /** The result of an integer division. */ myMath.DivResult = class { // Constructors are special; member fields can be declared in their bodies. constructor() { /** @type {number} */ this.quotient; /** @type {number} */ this.remainder; } // Methods can be declared as usual; their bodies are meaningless though. /** @return {!Array<number>} */ toPair() {} }; // Fields and methods can also be declared using prototype notation. /** * @override * @param {number=} radix */ myMath.DivResult.prototype.toString = function(radix) {};
Il flag --externs
In genere, l'annotazione @externs
è il modo migliore per comunicare
al compilatore che un file contiene extern. Questi file possono essere inclusi
come normali file origine utilizzando il flag della riga di comando --js
,
Tuttavia, esiste un altro modo, più vecchio, per specificare i file esterni. Il flag
--externs
della riga di comando può essere utilizzato per passare i file
externs in modo esplicito. Questo metodo non è consigliato.
Utilizzo di tirocinanti
Gli extern di cui sopra possono essere utilizzati nel seguente modo.
/** * @fileoverview Do some math. */ /** * @param {number} x * @param {number} y * @return {number} */ export function greatestCommonDivisor(x, y) { while (y != 0) { const temp = y; // `myMath` is a global, it and `myMath.div` are never renamed. const result = myMath.div(x, y); // `remainder` is also never renamed on instances of `DivResult`. y = result.remainder; x = temp; } return x; }
Scopo delle esportazioni
Le esportazioni sono un altro meccanismo per assegnare nomi coerenti ai simboli dopo la compilazione. Sono meno utili degli esterni e spesso confusi. Per tutti i casi, tranne quelli semplici, è meglio evitarli.
Le esportazioni si basano sul fatto che Closure Compiler non modifica i valori letterali stringa. Se assegni un oggetto a una proprietà denominata utilizzando un valore letterale, l'oggetto sarà disponibile tramite il nome della proprietà anche dopo la compilazione.
Di seguito è riportato un semplice esempio.
/** * @fileoverview Do some math. */ // Note that the concept of module exports is totally unrelated. /** @return {number} */ export function myFunction() { return 5; } // This assignment ensures `myFunctionAlias` will be a global alias exposing `myFunction`, // even after compilation. window['myFunctionAlias'] = myFunction;
Se utilizzi la libreria Closure, le esportazioni possono essere dichiarate anche utilizzando le funzioni
goog.exportSymbol
e goog.exportProperty
.
Per ulteriori informazioni, consulta la documentazione della libreria Closure di queste funzioni. Tuttavia, tieni presente che hanno un supporto speciale per il compilatore e verranno completamente trasformati nell'output compilato.
Problemi con le esportazioni
Le esportazioni si differenziano dagli esterni in quanto creano solo un alias a cui i consumatori possono fare riferimento. All'interno del codice compilato, il simbolo esportato verrà comunque rinominato. Per questo motivo, i simboli esportati devono essere costanti, poiché la loro riassegnazione nel codice farebbe sì che l'alias esposto punti alla cosa sbagliata.
Questa sottigliezza nella ridenominazione è particolarmente complicata per quanto riguarda le proprietà delle istanze esportate.
In teoria, le esportazioni possono consentire una dimensione del codice inferiore rispetto agli extern, poiché i nomi lunghi possono comunque essere modificati in nomi più brevi all'interno del codice. In pratica, questi miglioramenti sono spesso molto lievi e non giustificano la confusione creata dalle esportazioni.
Inoltre, le esportazioni non forniscono un'API che i consumer possano seguire come fanno gli extern. Rispetto alle esportazioni, gli extern documentano i simboli che intendi esporre, i loro tipi e ti offrono un posto in cui aggiungere informazioni sull'utilizzo. Inoltre, se i tuoi consumatori utilizzano anche Closure Compiler, avranno bisogno di externs per la compilazione.