Externas y exportaciones

Propósitos de los externos

Los externs son declaraciones que le indican al Closure Compiler los nombres de símbolos a los que no se debe cambiar el nombre durante la compilación avanzada. Se llaman externs porque estos símbolos se suelen definir con código fuera de la compilación, como código nativo o bibliotecas de terceros. Por esta razón, los externos también suelen tener anotaciones de tipo, por lo que Closure Compiler puede comprobar tu 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 alguna parte del código compilado. Los externs definen lo que el implementador promete comprometerse y lo que los consumidores pueden depender. Ambas partes necesitan una copia del contrato.

Los elementos externos son similares a los archivos de encabezado en otros idiomas.

Sintaxis Externs

Los elementos externalizados son archivos que se parecen mucho a JavaScript normal anotado para Closure Compiler. La diferencia principal es que su contenido nunca se imprime como parte de la salida compilada, 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 de origen normales mediante la marca de línea de comandos --js.

Sin embargo, hay otra forma más antigua de especificar archivos extern. La marca de línea de comandos de --externs se puede usar para pasar archivos de externs de manera explícita. No se recomienda este método.

Cómo usar elementos externos

Los externalizados de arriba pueden consumirse 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;
}
    

Cómo incluir elementos externos con la API de Closure Compiler Service

La aplicación Closure Compiler y la API del servicio de Closure Compiler permiten declaraciones externas. Sin embargo, la IU del servicio de Closure Compiler no proporciona un elemento de interfaz para especificar archivos de externs.

Existen tres maneras de enviar una declaración externa a un servicio de Closure Compiler:

  • Pasa un archivo que contenga la anotación @externs como un archivo de origen.
  • Pasa JavaScript al servicio de Closure Compiler en el parámetro js_externs.
  • Pasa la URL de un archivo JavaScript al servicio de Closure Compiler en el parámetro externs_url.

La única diferencia entre usar js_externs y externs_url es cómo se comunica JavaScript con el servicio de Closure Compiler.

Propósito de las exportaciones

Las exportaciones son otro mecanismo para dar a los símbolos nombres coherentes después de la compilación. Son menos útiles que los externos y, a menudo, son confusos. Para todos los casos, excepto los simples, es mejor evitarlos.

Las exportaciones se basan en el hecho de que Closure Compiler no modifica los literales de string. Cuando asignas un objeto a una propiedad nombrada 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 la biblioteca de cierre, 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 sobre la biblioteca de cierres de 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 externos, ya que solo crean un alias expuesto para que los consumidores puedan consultarlo. Dentro del código compilado, se cambiará el nombre del símbolo exportado. Por este motivo, los símbolos exportados deben ser constantes, ya que si los reasignas en tu código, el alias expuesto podría apuntar a algo incorrecto.

Esta sutileza con el cambio de nombre es especialmente complicada en relación con 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 externs, ya que los nombres largos aún se pueden cambiar a nombres más cortos dentro de tu código. En la práctica, estas mejoras suelen ser muy leves y no justifican la confusión que generan las exportaciones.

Las exportaciones tampoco proporcionan una API para que los consumidores sigan el modo en que lo hacen los externs. En comparación con las exportaciones, los externos documentan los símbolos que deseas exponer, sus tipos y te proporcionan un lugar para agregar información de uso. Además, si tus consumidores también usan Closure Compiler, necesitarán recursos externos para compilar.