Objetivo de las prácticas externas
Los externs son declaraciones que le indican al compilador de Closure los nombres de los símbolos que no se deben cambiar durante la compilación avanzada. Se denominan externos porque estos símbolos se definen con mayor frecuencia por código fuera de la compilación, como código nativo o bibliotecas de terceros. Por este motivo, los externos a menudo también tienen anotaciones de tipo, de modo que Closure Compiler pueda verificar el tipo de uso de esos símbolos.
En general, es mejor pensar en los externs como un contrato de API entre el implementador y los consumidores de algún fragmento de código compilado. Los externs definen lo que el implementador promete proporcionar y lo que los consumidores pueden usar. Ambas partes necesitan una copia del contrato.
Los externs son similares a los archivos de encabezado en otros lenguajes.
Sintaxis de Externs
Los archivos externos son archivos que se parecen mucho al JavaScript normal anotado para el compilador de Closure. La principal diferencia es que su contenido nunca se imprime como parte del resultado compilado, por lo que ninguno de los valores es significativo, solo los nombres y los tipos.
A continuación, se muestra un ejemplo de un archivo de externs para una biblioteca simple.
// 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) {};
La marca --externs
En general, la anotación @externs
es la mejor manera de informar al compilador que un archivo contiene elementos externos. Estos archivos se pueden incluir como archivos fuente normales con la marca de línea de comandos --js
.
Sin embargo, existe otra forma más antigua de especificar archivos de externs. La marca de línea de comandos --externs
se puede usar para pasar archivos de elementos externos de forma explícita. No se recomienda este método.
Uso de Externs
Los elementos externos anteriores se pueden consumir de la siguiente manera.
/** * @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; }
Propósito de las exportaciones
Las exportaciones son otro mecanismo para asignar nombres coherentes a los símbolos después de la compilación. Son menos útiles que los externos y, a menudo, resultan confusos. Para todos los casos, excepto los simples, es mejor evitarlos.
Las exportaciones se basan en el hecho de que el compilador de Closure no modifica los literales de cadena. Si asignas un objeto a una propiedad cuyo nombre se asigna con un literal, el objeto estará disponible a través de ese nombre de propiedad incluso después de la compilación.
A continuación, se muestra un ejemplo simple.
/** * @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;
Si usas Closure Library, las exportaciones también se pueden declarar con las funciones goog.exportSymbol
y goog.exportProperty
.
Puedes encontrar más información en la documentación de la biblioteca de Closure sobre estas funciones. Sin embargo, ten en cuenta que tienen compatibilidad especial con el compilador y se transformarán por completo en el resultado compilado.
Problemas con las exportaciones
Las exportaciones se diferencian de los externs en que solo crean un alias expuesto para que los consumidores hagan referencia a él. Dentro del código compilado, el símbolo exportado se seguirá renombrando. Por este motivo, los símbolos exportados deben ser constantes, ya que reasignarlos en tu código haría que el alias expuesto apunte al elemento incorrecto.
Esta sutileza en el cambio de nombre es especialmente complicada con respecto a las propiedades de instancia exportadas.
En teoría, las exportaciones pueden permitir un tamaño de código más pequeño en comparación con los externos, ya que los nombres largos se pueden cambiar por otros más cortos dentro de tu código. En la práctica, estas mejoras suelen ser muy pequeñas y no justifican la confusión que generan las exportaciones.
Las exportaciones tampoco proporcionan una API para que los consumidores la sigan de la misma manera que lo hacen los externs. En comparación con las exportaciones, los externs documentan los símbolos que deseas exponer, sus tipos y te brindan un lugar para agregar información de uso. Además, si tus consumidores también usan Closure Compiler, necesitarán archivos externos para compilar.