Extern und Exporte

Zweck von Extern

Extern sind Deklarationen, die Closure Compiler den Namen von Symbolen mitteilen, die während der erweiterten Kompilierung nicht umbenannt werden sollen. Sie werden als „extern“ bezeichnet, da sie meist durch Code außerhalb der Kompilierung, z. B. nativen Code oder Drittanbieterbibliotheken, definiert werden. Aus diesem Grund haben extern häufig Typanmerkungen, sodass Closure Compiler die Verwendung dieser Symbole in einem Typ überprüfen kann.

Im Allgemeinen ist extern ein API-Vertrag zwischen dem Implementierer und den Nutzern eines kompilierten Codes. In den Externs wird festgelegt, was der Implementierer versprechen und was die Verbraucher davon abhängig machen können. Beide Seiten benötigen eine Kopie des Vertrags.

Extern funktionieren die Header-Dateien in anderen Sprachen ähnlich.

Externs-Syntax

Extern sind Dateien, die dem normalen JavaScript-Code für Closure Compiler ähneln. Der Hauptunterschied besteht darin, dass deren Inhalt nie als Teil der kompilierten Ausgabe gedruckt wird, sodass keiner der Werte aussagekräftig ist, sondern nur die Namen und Typen.

Unten sehen Sie ein Beispiel für eine externe Datei für eine einfache Bibliothek.

// 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) {};
    

Flag --externs

Im Allgemeinen ist die Annotation @externs die beste Möglichkeit, den Compiler darüber zu informieren, dass eine Datei externe Dateien enthält. Solche Dateien können mit dem --js-Befehlszeilentool als normale Quelldateien hinzugefügt werden.

Es gibt jedoch eine andere, ältere Möglichkeit, externe Dateien anzugeben. Mit dem Befehlszeilen-Flag --externs können externe Dateien explizit übergeben werden. Diese Methode wird nicht empfohlen.

Extern verwenden

Die oben aufgeführten Beispiele können so verwendet werden.

/**
 * @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;
}
    

Extern mit der Closure Compiler Service API einbinden

Sowohl die Closure Compiler App als auch die Closure Compiler Service API lassen externe Deklarationen zu. Die UI des Compiler-Diensts bietet jedoch kein Schnittstellenelement zum Angeben externer Dateien.

Es gibt drei Möglichkeiten, eine externe Deklaration an den Closure-Compiler-Dienst zu senden:

  • Übergeben Sie eine Datei mit der Annotation @externs als Quelldatei.
  • Übergeben Sie im Parameter js_externs den JavaScript-Code an den Closure-Compiler-Dienst.
  • Übergeben Sie die URL einer JavaScript-Datei im externs_url-Parameter an den Closure-Compiler-Dienst.

Der einzige Unterschied zwischen der Verwendung von js_externs und externs_url besteht darin, wie JavaScript an den Closure Compiler-Dienst kommuniziert wird.

Verwendungszwecke von Exporten

Exporte sind eine weitere Methode, um Symbolen nach der Kompilierung einheitliche Namen zu geben. Sie sind weniger hilfreich als extern und oft verwirrend. In allen Ausnahmefällen sollten sie sich am besten vermeiden.

Exporte basieren darauf, dass Closure Compiler Stringliterale nicht ändert. Wenn Sie ein Objekt einer Property mit einem Literal zuweisen, ist das Objekt über diesen Property-Namen auch nach der Kompilierung verfügbar.

Unten sehen Sie ein einfaches Beispiel.

/**
 * @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;
    

Wenn Sie die Closure Library verwenden, können Exporte auch mit den Funktionen goog.exportSymbol und goog.exportProperty deklariert werden.

Weitere Informationen finden Sie in der Dokumentation zur Closure Library dieser Funktionen. Beachten Sie jedoch, dass sie eine spezielle Compiler-Unterstützung bieten und in der kompilierten Ausgabe komplett umgewandelt werden.

Probleme mit Exporten

Exporte unterscheiden sich von extern, da sie nur einen exponierten Alias für Nutzer erstellen, auf den verwiesen werden kann. Im kompilierten Code wird das exportierte Symbol weiterhin umbenannt. Aus diesem Grund müssen exportierte Symbole konstant bleiben, da eine Neuzuweisung des bereitgestellten Alias dazu führen würde, dass auf das falsche Element verwiesen wird.

Diese Feinheiten bei der Umbenennung sind in Bezug auf exportierte Instanzattribute besonders kompliziert.

Theoretisch können Exporte eine kleinere Codegröße als extern zulassen, da lange Namen innerhalb Ihres Codes in kürzere Namen geändert werden können. In der Praxis sind diese Verbesserungen häufig nur sehr gering und rechtfertigen nicht die Verwirrung beim Export.

Exporte bieten auch keine API, mit der Nutzer wie externe Funktionen folgen können. Im Vergleich zu Exporten dokumentieren extern die Symbole, die Sie veröffentlichen möchten, und deren Typen und bieten Ihnen die Möglichkeit, Nutzungsinformationen hinzuzufügen. Wenn Ihre Nutzer außerdem Closure Compiler verwenden, benötigen sie externe Komponenten für die Kompilierung.