出口與出口

實習生的目的

外部項是宣告,可告知 Closure 編譯器在進階編譯期間,哪些符號名稱不應重新命名。這些符號最常由編譯外的程式碼 (例如原生程式碼或第三方程式庫) 定義,因此稱為外部符號。因此,外部函式通常也會有型別註解,方便 Closure 編譯器檢查您對這些符號的使用情形。

一般來說,最好將外部函式視為實作人員與某段已編譯程式碼的消費者之間的 API 合約。外部定義實作人員承諾提供的內容,以及消費者可依賴使用的內容。雙方都需要合約副本。

外部檔案與其他語言的標頭檔案類似。

Externs 語法

外部函式是類似於一般 JavaScript 的檔案,但會針對 Closure 編譯器加上註解。主要差異在於,編譯輸出內容中絕不會列印這些內容,因此這些值都沒有意義,只有名稱和型別有意義。

以下是簡單程式庫的外部檔案範例。

// 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 註解是告知編譯器檔案含有外部項目的最佳方式。這類檔案可使用 --js 指令列旗標,以一般來源檔案的形式納入,

不過,您也可以使用較舊的方法指定外部檔案。--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;
}
    

匯出目的

匯出是另一種機制,可在編譯後為符號提供一致的名稱。這類函式不如外部函式實用,而且通常會造成混淆。除了簡單的情況外,最好避免使用。

匯出作業的依據是 Closure 編譯器不會修改字串常值。 將物件指派給以常值命名的屬性後,即使在編譯後,該物件仍可透過該屬性名稱使用。

以下是簡單的範例。

/**
 * @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.exportSymbolgoog.exportProperty 函式宣告匯出內容。

如要進一步瞭解這些函式,請參閱 Closure 程式庫說明文件。不過請注意,這些標記需要特殊的編譯器支援, 且會在編譯輸出內容中完全轉換。

匯出作業相關問題

匯出內容與外部內容不同,因為匯出內容只會建立供消費者參照的公開別名。在編譯後的程式碼中,匯出的符號仍會重新命名。因此,匯出的符號必須是常數,因為在程式碼中重新指派這些符號,會導致公開別名指向錯誤的項目。

如果重新命名的是匯出的例項屬性,這項細微差異就會變得特別複雜。

理論上,與外部函式相比,匯出功能可縮減程式碼大小,因為長名稱仍可在程式碼中變更為較短的名稱。但實際上,這些改善通常非常微小,不足以證明匯出作業造成的混淆是合理的。

此外,匯出內容不會提供 API,供消費者以外部項目的方式追蹤。 相較於匯出內容,外部項目會記錄您要公開的符號、這些符號的類型,並提供新增使用資訊的位置。此外,如果消費者也使用 Closure Compiler,則需要外部函式來編譯。