Xuất và xuất

Mục đích của Extern

Extern là các khai báo cho Closure Compiler biết tên của những ký hiệu không được đổi tên trong quá trình biên dịch nâng cao. Chúng được gọi là extern vì những biểu tượng này thường được xác định bằng mã bên ngoài quá trình biên dịch, chẳng hạn như mã gốc hoặc thư viện của bên thứ ba. Vì lý do này, các extern cũng thường có chú giải kiểu, để Closure Compiler có thể kiểm tra kiểu việc sử dụng các biểu tượng đó.

Nhìn chung, bạn nên coi các extern là một hợp đồng API giữa người triển khai và người dùng của một đoạn mã đã biên dịch. Các extern xác định những gì mà người triển khai hứa hẹn cung cấp và những gì mà người dùng có thể dựa vào để sử dụng. Cả hai bên đều cần có bản sao hợp đồng.

Extern tương tự như tệp tiêu đề trong các ngôn ngữ khác.

Cú pháp Externs

Extern là những tệp trông rất giống với JavaScript thông thường được chú thích cho Trình biên dịch Closure. Điểm khác biệt chính là nội dung của các phương diện này không bao giờ được in dưới dạng một phần của đầu ra đã biên dịch, vì vậy, không có giá trị nào có ý nghĩa, chỉ có tên và loại.

Dưới đây là ví dụ về tệp externs cho một thư viện đơn giản.

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

Cờ --externs

Nhìn chung, chú giải @externs là cách tốt nhất để thông báo cho trình biên dịch rằng một tệp chứa các extern. Bạn có thể đưa các tệp như vậy vào dưới dạng tệp nguồn thông thường bằng cách sử dụng cờ dòng lệnh --js,

Tuy nhiên, có một cách khác (cách cũ hơn) để chỉ định tệp extern. Bạn có thể dùng cờ dòng lệnh --externs để truyền tệp externs một cách rõ ràng. Bạn không nên sử dụng phương thức này.

Sử dụng Extern

Bạn có thể sử dụng các extern ở trên như sau.

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

Mục đích xuất khẩu

Xuất là một cơ chế khác để đặt tên nhất quán cho các biểu tượng sau khi biên dịch. Chúng ít hữu ích hơn so với các extern và thường gây nhầm lẫn. Đối với tất cả các trường hợp, trừ những trường hợp đơn giản, bạn nên tránh sử dụng các trường hợp này.

Các hàm xuất dựa vào thực tế là Trình biên dịch Closure không sửa đổi các giá trị cố định chuỗi. Bằng cách chỉ định một đối tượng cho một thuộc tính được đặt tên bằng một giá trị cố định, đối tượng sẽ có sẵn thông qua tên thuộc tính đó ngay cả sau khi biên dịch.

Dưới đây là một ví dụ đơn giản.

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

Nếu đang sử dụng Thư viện Closure, bạn cũng có thể khai báo các hàm xuất bằng cách sử dụng các hàm goog.exportSymbolgoog.exportProperty.

Bạn có thể xem thêm thông tin trong tài liệu về Thư viện Closure của các hàm này. Tuy nhiên, hãy lưu ý rằng các đối tượng này có sự hỗ trợ đặc biệt của trình biên dịch và sẽ được chuyển đổi hoàn toàn trong đầu ra đã biên dịch.

Vấn đề khi xuất

Các giá trị xuất khác với các giá trị bên ngoài ở chỗ chúng chỉ tạo một bí danh được hiển thị để người dùng tham chiếu. Trong mã đã biên dịch, biểu tượng đã xuất vẫn sẽ được đổi tên. Vì lý do này, các biểu tượng đã xuất phải là hằng số, vì việc gán lại các biểu tượng đó trong mã của bạn sẽ khiến bí danh được hiển thị trỏ đến nội dung không chính xác.

Sự tinh tế này trong việc đổi tên đặc biệt phức tạp đối với các thuộc tính phiên bản được xuất.

Về lý thuyết, các tệp xuất có thể cho phép kích thước mã nhỏ hơn so với các tệp bên ngoài, vì tên dài vẫn có thể được thay đổi thành tên ngắn hơn trong mã của bạn. Trên thực tế, những điểm cải tiến này thường rất nhỏ và không đủ để biện minh cho sự nhầm lẫn mà các tệp xuất tạo ra.

Các tệp xuất cũng không cung cấp API để người dùng có thể làm theo như các tệp bên ngoài. So với các tệp xuất, các tệp bên ngoài ghi lại những biểu tượng mà bạn dự định hiển thị, các loại biểu tượng và cung cấp cho bạn một nơi để thêm thông tin sử dụng. Ngoài ra, nếu người dùng của bạn cũng đang sử dụng Closure Compiler, thì họ sẽ cần các tệp bên ngoài để biên dịch.