Eksporty i eksporty

Cel badań

Extery to deklaracje, które informują kompilator zamknięty o nazwach symboli, których nie należy zmieniać podczas zaawansowanej kompilacji. Są one nazywane rozszerzeniami, ponieważ najczęściej są definiowane przez kod spoza kompilacji, taki jak kod natywny lub biblioteki zewnętrzne. Z tego powodu dostawcy zewnętrzni mają też adnotacje, które pozwalają sprawdzać, czy te symbole są używane.

Ogólnie rzecz biorąc, oprogramowanie zewnętrzne należy traktować jako umowę między interfejsem API a konsumentami skompilowanego kodu. Pracownicy zewnętrzni określają, jakie dane będą przekazywane przez wdrożenia i na czym mogą polegać. Obie strony muszą mieć kopię umowy.

Rozszerzenia są podobne do plików nagłówka w innych językach.

Składnia zewnątrz

Pliki zastępcze to pliki, które wyglądają jak zwykłe adnotacje JavaScript używane w komponencie Closure Compiler. Główna różnica polega na tym, że ich zawartość nigdy nie jest drukowana jako część skompilowanych danych wyjściowych, więc żadna z nich nie ma znaczenia, a jedynie nazwy i typy.

Poniżej znajduje się przykład pliku rozszerzeń z prostą biblioteką.

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

Flaga --externs

Adnotacje @externs to najlepsze sposoby informowania kompilatora, że plik zawiera rozszerzenia. Takie pliki można umieścić jako normalne pliki źródłowe za pomocą flagi wiersza poleceń --js.

Istnieje jednak inny, starszy sposób określania plików rozszerzeń. Flagi wiersza poleceń --externs można używać do bezpośredniego przekazywania plików rozszerzeń. Ta metoda nie jest zalecana.

Korzystanie z zewnętrznych źródeł

Rozszerzenia z powyżej można skorzystać w następujący sposób.

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

Jak uwzględniać zewnętrzne interfejsy API za pomocą interfejsu Closure Compiler Service API

Zarówno aplikacja Closure Compiler, jak i interfejs API usługi Closure Compiler zezwalają na deklaracje zewnętrzne. Jednak interfejs usługi Closure Compiler nie udostępnia elementu interfejsu służącego do określania plików rozszerzeń.

Istnieją 3 sposoby wysyłania deklaracji zewnętrznej do usługi kompilatora zamkniętych:

  • Prześlij plik źródłowy zawierający adnotację @externs.
  • Przekaż kod JavaScript do usługi Closure Compiler w parametrze js_externs.
  • Przekaż adres URL pliku JavaScript do usługi kompozytora Closure Compiler w parametrze externs_url.

Jedyna różnica między użyciem js_externs a externs_url to sposób komunikacji kodu JavaScript z usługą Compute Compiler.

Cel eksportu

Innym sposobem na zapewnienie jednolitych nazw po skompilowaniu jest eksport. Są mniej przydatne od zewnętrznych i często wprowadzają w błąd. Najlepiej jest unikać wszystkich przypadków oprócz prostych.

Eksport polega na tym, że kompozytor Closure nie modyfikuje literałów ciągów. Gdy przypiszesz obiekt do właściwości o nazwie literału, będzie on dostępny dzięki tej właściwości, nawet po kompilacji.

Oto prosty przykład.

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

Jeśli korzystasz z biblioteki Closure, eksporty można również zadeklarować za pomocą funkcji goog.exportSymbol i goog.exportProperty.

Więcej informacji znajdziesz w dokumentacji biblioteki funkcji Closure. Pamiętaj jednak, że mają one szczególne funkcje kompilacji i zostaną całkowicie przekształcone w skompilowane dane wyjściowe.

Problemy z eksportem

Eksporty różnią się od zewnętrznych, ponieważ tworzą tylko alias, który jest dostępny dla konsumentów. W skompilowanym kodzie nadal będzie widoczna nazwa wyeksportowanego symbolu. Z tego powodu wyeksportowane symbole muszą być stałe, ponieważ ich ponowne przypisanie w kodzie spowodowałoby wskazywanie niewłaściwego aliasu na inną.

Ten subtelny sposób zmiany nazwy jest szczególnie złożony w odniesieniu do właściwości eksportowanych instancji.

Teoretycznie eksporty mogą zezwalać na sprawdzanie kodu o mniejszym rozmiarze niż rozszerzenia, ponieważ długie nazwy można nadal zmieniać na krótsze. W praktyce poprawki te są często bardzo niewielkie i nie uzasadniają tworzenia niejasnych efektów eksportu.

Eksporty nie udostępniają też interfejsu API dla klientów w sposób zgodny z zasadami eksportowanych przez nich. W porównaniu z eksportami eksporterzy przypisują do dokumentów symbole, które mają być widoczne, ich typy, a także miejsce, w którym można dodać informacje o użytkowaniu. Oprócz tego, jeśli Twoi klienci korzystają również z kompilatora Closure Compiler, będą musieli skorzystać z zewnętrznych narzędzi do kompilacji.