การส่งออกและการส่งออก

วัตถุประสงค์ของโปรแกรมฝึกงาน

Extern คือการประกาศที่บอก Closure Compiler ถึงชื่อของ สัญลักษณ์ที่ไม่ควรเปลี่ยนชื่อในระหว่างการคอมไพล์ขั้นสูง เราเรียกสัญลักษณ์เหล่านี้ว่า "ภายนอก" เนื่องจากสัญลักษณ์เหล่านี้มักจะกำหนดโดย โค้ดภายนอกการคอมไพล์ เช่น โค้ดดั้งเดิม หรือไลบรารีของบุคคลที่สาม ด้วยเหตุนี้ ไฟล์ภายนอกจึงมักจะมีคำอธิบายประกอบประเภทด้วย เพื่อให้ Closure Compiler ตรวจสอบประเภทการใช้สัญลักษณ์เหล่านั้นได้

โดยทั่วไปแล้ว คุณควรคิดว่า Externs เป็นสัญญา API ระหว่าง ผู้ใช้และผู้บริโภคของโค้ดที่คอมไพล์แล้ว externs จะกำหนดสิ่งที่ผู้ใช้สัญญาว่าจะจัดหาให้ และสิ่งที่ ผู้บริโภคสามารถใช้ได้ ทั้ง 2 ฝ่ายต้องมีสำเนาสัญญา

Externs จะคล้ายกับไฟล์ส่วนหัวในภาษาอื่นๆ

ไวยากรณ์ Externs

Externs คือไฟล์ที่มีลักษณะคล้ายกับ 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 Flag บรรทัดคำสั่งเพื่อส่งไฟล์ externs อย่างชัดเจน เราไม่แนะนำให้ใช้วิธีนี้

การใช้ Externs

คุณใช้ Extern จากด้านบนได้ดังนี้

/**
 * @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 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 Library คุณยังประกาศการส่งออกได้โดยใช้ฟังก์ชัน goog.exportSymbol และ goog.exportProperty

ดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบของ Closure Library เกี่ยวกับ ฟังก์ชันเหล่านี้ อย่างไรก็ตาม โปรดทราบว่าองค์ประกอบเหล่านี้มีการรองรับคอมไพเลอร์พิเศษ และจะได้รับการแปลงอย่างสมบูรณ์ในเอาต์พุตที่คอมไพล์แล้ว

ปัญหาเกี่ยวกับการส่งออก

การส่งออกแตกต่างจาก Extern ตรงที่การส่งออกจะสร้างเฉพาะนามแฝงที่เปิดเผยเพื่อให้ผู้ใช้ทั่วไปอ้างอิง ภายในโค้ดที่คอมไพล์แล้ว ระบบจะยังคงเปลี่ยนชื่อสัญลักษณ์ exported ด้วยเหตุนี้ สัญลักษณ์ที่ส่งออกจึงต้องเป็นค่าคงที่ เนื่องจากการกำหนดค่าใหม่ในโค้ดจะทำให้นามแฝงที่เปิดเผย ชี้ไปยังสิ่งที่ไม่ถูกต้อง

ความซับซ้อนในการเปลี่ยนชื่อนี้จะยิ่งซับซ้อนมากขึ้นเมื่อเกี่ยวข้องกับพร็อพเพอร์ตี้อินสแตนซ์ที่ส่งออก

ในทางทฤษฎี การส่งออกช่วยให้มีขนาดโค้ดเล็กลงได้เมื่อเทียบกับ Extern เนื่องจากยังเปลี่ยนชื่อที่ยาวให้สั้นลงได้ภายในโค้ด ในทางปฏิบัติ การปรับปรุงเหล่านี้มักเป็นการปรับปรุงเล็กๆ น้อยๆ และไม่คุ้มค่ากับความสับสนที่การส่งออกสร้างขึ้น

นอกจากนี้ การส่งออกยังไม่มี API ให้ผู้บริโภคทำตามในลักษณะเดียวกับที่ Externs ทำ เมื่อเทียบกับการส่งออก Externs จะบันทึกสัญลักษณ์ที่คุณต้องการแสดง ประเภทของสัญลักษณ์ และให้พื้นที่แก่คุณเพื่อเพิ่มข้อมูลการใช้งาน นอกจากนี้ หากผู้บริโภคใช้ Closure Compiler ด้วย ก็จะต้องมีไฟล์ภายนอกเพื่อ คอมไพล์