Экстерны и экспорт

Назначение экстернов

Экстерны — это объявления, которые сообщают компилятору Closure имена символов, которые не следует переименовывать во время расширенной компиляции. Они называются внешними, потому что эти символы чаще всего определяются кодом вне компиляции, таким как собственный код или сторонние библиотеки. По этой причине внешние элементы часто также имеют аннотации типов, чтобы компилятор Closure мог проверить использование вами этих символов.

В общем, лучше всего думать о экстернах как о контракте API между разработчиком и потребителями некоторого фрагмента скомпилированного кода. Экстерны определяют, что разработчик обещает предоставить, и от чего могут зависеть потребители. Обеим сторонам нужна копия контракта.

Внешние аналогичны заголовочным файлам в других языках.

Внешний вид Синтаксис

Внешние файлы — это файлы, очень похожие на обычный JavaScript, аннотированный для Closure Compiler. Основное отличие состоит в том, что их содержимое никогда не печатается как часть скомпилированного вывода, поэтому ни одно из значений не имеет смысла, только имена и типы.

Ниже приведен пример файла externs для простой библиотеки.

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

Флаг --externs

Как правило, аннотация @externs — лучший способ сообщить компилятору, что файл содержит externs. Такие файлы могут быть включены как обычные исходные файлы с использованием флага командной строки --js

Однако есть и другой, более старый способ указания файлов externs. Флаг командной строки --externs можно использовать для явной передачи файлов externs. Этот метод не рекомендуется.

Использование внешних

Экстерны, указанные выше, можно потреблять следующим образом.

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

Как включить внешние компоненты в API службы компилятора закрытия

И приложение Closure Compiler, и API-интерфейс службы Closure Compiler допускают объявления extern. Однако пользовательский интерфейс службы Closure Compiler не предоставляет элемент интерфейса для указания файлов externs.

Есть три способа отправить объявление extern службе Closure Compiler:

  • Передайте файл, содержащий аннотацию @externs , в качестве исходного файла.
  • Передайте JavaScript службе Closure Compiler в параметре js_externs .
  • Передайте URL-адрес файла JavaScript службе Closure Compiler в параметре externs_url .

Единственная разница между использованием js_externs и externs_url заключается в том, как JavaScript передается службе Closure Compiler.

Цель экспорта

Экспорты — еще один механизм присвоения символам согласованных имен после компиляции. Они менее полезны, чем внешние, и часто сбивают с толку. Во всех случаях, кроме простых, их лучше избегать.

Экспорт основан на том факте, что Closure Compiler не изменяет строковые литералы. При назначении объекта свойству, названному с использованием литерала, объект будет доступен через это имя свойства даже после компиляции.

Ниже приведен простой пример.

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

Если вы используете библиотеку Closure, экспорт также можно объявить с помощью goog.exportSymbol и goog.exportProperty .

Более подробная информация доступна в документации Closure Library по этим функциям. Однако имейте в виду, что они имеют специальную поддержку компилятора и будут полностью преобразованы в скомпилированном выводе.

Проблемы с экспортом

Экспорты отличаются от внешних тем, что они создают только открытый псевдоним, на который могут ссылаться потребители. В скомпилированном коде экспортируемый символ все равно будет переименован. По этой причине экспортируемые символы должны быть постоянными, поскольку их переназначение в вашем коде приведет к тому, что открытый псевдоним будет указывать на неправильную вещь.

Эта тонкость переименования особенно сложна в отношении экспортируемых свойств экземпляра.

Теоретически экспорт может позволить меньший размер кода по сравнению с внешними, поскольку длинные имена все еще могут быть изменены на более короткие в вашем коде. На практике эти улучшения часто очень незначительны и не оправдывают путаницу, которую создает экспорт.

Экспорты также не предоставляют потребителям API, которым они могли бы следовать, как это делают внешние. По сравнению с экспортом, экстерны документируют символы, которые вы собираетесь отображать, их типы и дают вам место для добавления информации об использовании. Кроме того, если ваши потребители также используют Closure Compiler, им потребуются внешние компоненты для компиляции.