الغرض من برنامج التدريب الخارجي
Externs هي تعريفات تخبر Closure Compiler بأسماء الرموز التي يجب عدم إعادة تسميتها أثناء عملية التجميع المتقدّمة. ويُطلق عليها اسم "الرموز الخارجية" لأنّ هذه الرموز يتم تعريفها غالبًا من خلال رمز برمجي خارج عملية التجميع، مثل الرمز البرمجي الأصلي أو المكتبات التابعة لجهات خارجية. لهذا السبب، غالبًا ما تتضمّن العناصر الخارجية أيضًا تعليقات توضيحية للأنواع، ليتمكّن Closure Compiler من التحقّق من أنواع استخدامك لهذه الرموز.
بشكل عام، من الأفضل اعتبار externs بمثابة عقد واجهة برمجة تطبيقات بين المنفِّذ ومستهلكي جزء من الرمز البرمجي المجمَّع. تحدّد 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
أفضل طريقة لإعلام المترجم بأنّ الملف يتضمّن تعريفات خارجية. ويمكن تضمين هذه الملفات كملفات مصدر عادية باستخدام علامة سطر الأوامر --js
.
ومع ذلك، هناك طريقة أخرى أقدم لتحديد ملفات externs. يمكن استخدام علامة سطر الأوامر --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; }
الغرض من عمليات التصدير
عمليات التصدير هي آلية أخرى لمنح الرموز أسماء متسقة بعد الترجمة البرمجية. وهي أقل فائدة من externs وغالبًا ما تكون مربكة. ويُنصح بتجنُّبها في كل الحالات باستثناء الحالات البسيطة.
تعتمد عمليات التصدير على حقيقة أنّ 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 الخاصة بهذه الدوال. ومع ذلك، يجب الانتباه إلى أنّها تتوافق مع برامج ترجمة خاصة وسيتم تحويلها بالكامل في الناتج المترجَم.
مشاكل متعلقة بعمليات التصدير
تختلف عمليات التصدير عن عمليات التصدير الخارجية في أنّها تنشئ اسمًا مستعارًا مكشوفًا فقط يمكن للمستهلكين الرجوع إليه. سيتم إعادة تسمية الرمز الذي تم تصديره ضمن الرمز المجمّع. لهذا السبب، يجب أن تكون الرموز التي يتم تصديرها ثابتة، لأنّ إعادة تعيينها في الرمز البرمجي سيؤدي إلى أن يشير الاسم المستعار المعروض إلى العنصر الخاطئ.
ويزداد تعقيد هذه الدقة في إعادة التسمية خاصةً فيما يتعلق بخصائص الكائنات التي تم تصديرها.
من الناحية النظرية، يمكن أن تسمح عمليات التصدير بحجم رمز أصغر مقارنةً بالملفات الخارجية، لأنّه يمكن تغيير الأسماء الطويلة إلى أسماء أقصر داخل الرمز. في الواقع، غالبًا ما تكون هذه التحسينات طفيفة جدًا، ولا تبرّر الالتباس الذي تسبّبه عمليات التصدير.
ولا توفّر عمليات التصدير أيضًا واجهة برمجة تطبيقات للمستهلكين يمكنهم اتّباعها بالطريقة التي توفّرها الرموز الخارجية. بالمقارنة مع عمليات التصدير، توثّق الرموز الخارجية الرموز التي تريد عرضها، وأنواعها، وتوفّر لك مكانًا لإضافة معلومات الاستخدام. بالإضافة إلى ذلك، إذا كان المستهلكون يستخدمون Closure Compiler أيضًا، سيحتاجون إلى ملفات تعريف خارجية لتجميع الرمز البرمجي.