Extern und Exporte

Zweck von externen Mitarbeitern

Externs sind Deklarationen, die Closure Compiler die Namen von Symbolen mitteilen, die während der erweiterten Kompilierung nicht umbenannt werden sollen. Sie werden als „externs“ bezeichnet, da diese Symbole meistens durch Code außerhalb der Kompilierung definiert werden, z. B. durch nativen Code oder Drittanbieterbibliotheken. Aus diesem Grund haben Externs oft auch Typanmerkungen, damit Closure Compiler die Verwendung dieser Symbole auf Typfehler prüfen kann.

Im Allgemeinen ist es am besten, sich Externs als API-Vertrag zwischen dem Implementierer und den Nutzern eines kompilierten Codeabschnitts vorzustellen. Die Externs definieren, was der Implementierer bereitstellt und was die Nutzer verwenden können. Beide Seiten benötigen eine Kopie des Vertrags.

Externs ähneln Header-Dateien in anderen Sprachen.

Externs-Syntax

Externs sind Dateien, die normalen JavaScript-Dateien sehr ähnlich sind, aber für den Closure Compiler annotiert wurden. Der Hauptunterschied besteht darin, dass ihr Inhalt nie als Teil der kompilierten Ausgabe ausgegeben wird. Daher sind keine der Werte aussagekräftig, sondern nur die Namen und Typen.

Unten sehen Sie ein Beispiel für eine Externs-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) {};
    

Das Flag --externs

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

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

Externs verwenden

Die externen Dateien von oben 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;
}
    

Zweck der Exporte

Exporte sind ein weiterer Mechanismus, um Symbolen nach der Kompilierung konsistente Namen zu geben. Sie sind weniger nützlich als Externs und oft verwirrend. In allen Fällen, die nicht einfach sind, sollten sie am besten vermieden werden.

Exporte basieren darauf, dass der Closure Compiler Stringliterale nicht ändert. Wenn Sie einer Property, die mit einem Literal benannt ist, ein Objekt zuweisen, ist das Objekt auch nach der Kompilierung über diesen Property-Namen 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-Bibliothek verwenden, können Exporte auch mit den Funktionen goog.exportSymbol und goog.exportProperty deklariert werden.

Weitere Informationen finden Sie in der Closure-Bibliotheksdokumentation zu diesen Funktionen. Sie benötigen jedoch eine spezielle Compilerunterstützung und werden in der kompilierten Ausgabe vollständig transformiert.

Probleme beim Exportieren

Exporte unterscheiden sich von Externs dadurch, dass sie nur einen Alias für die Nutzung durch andere erstellen. Im kompilierten Code wird das exportierte Symbol weiterhin umbenannt. Aus diesem Grund müssen exportierte Symbole konstant sein, da eine Neuzuweisung im Code dazu führen würde, dass der bereitgestellte Alias auf das falsche Element verweist.

Diese Feinheit bei der Umbenennung ist besonders kompliziert in Bezug auf exportierte Instanzeigenschaften.

Theoretisch können Exporte im Vergleich zu Externs zu einer kleineren Codegröße führen, da lange Namen in Ihrem Code weiterhin in kürzere Namen geändert werden können. In der Praxis sind diese Verbesserungen oft sehr gering und rechtfertigen nicht die Verwirrung, die durch Exporte entsteht.

Exporte bieten auch keine API, die Nutzer befolgen können, wie es bei Externs der Fall ist. Im Vergleich zu Exporten werden bei Externs die Symbole, die Sie verfügbar machen möchten, sowie deren Typen dokumentiert. Außerdem können Sie Nutzungsinformationen hinzufügen. Wenn Ihre Nutzer auch den Closure Compiler verwenden, benötigen sie außerdem Externs für die Kompilierung.