نقل النصوص البرمجية إلى وقت التشغيل V8

إذا كان لديك نص برمجي يستخدم بيئة تشغيل Rhino وترغب في الاستفادة من في بنية V8 وميزاته، يجب نقل النص البرمجي إلى V8.

يمكن تشغيل معظم النصوص البرمجية المكتوبة باستخدام وقت تشغيل Rhino باستخدام وقت تشغيل V8 بدون تعديل. غالبًا ما يكون الشرط الوحيد لإضافة بنية V8 و ميزاته إلى نص برمجي هو تفعيل وقت تشغيل V8.

ومع ذلك، هناك مجموعة صغيرة من التوافقات والاختلافات الأخرى التي يمكن أن تؤدي إلى تعطُّل ملف نصي أو سلوكه بشكل غير متوقّع بعد تفعيل وقت تشغيل V8. أثناء النقل نص برمجي لاستخدام V8، فيجب البحث في مشروع النص البرمجي عن هذه المشكلات وتصحيح أي أخطاء تجدها.

إجراء نقل البيانات إلى V8

لنقل نص برمجي إلى V8، اتّبِع الإجراء التالي:

  1. تفعيل بيئة تشغيل V8 للنص.
  2. راجِع بعناية حالات عدم التوافق المدرَجة أدناه. راجِع النص البرمجي لتحديد ما إذا كان يتضمّن أيًا من المشاكل المتعلقة بعدم التوافق. إذا كان يتضمّن مشكلة واحدة أو أكثر، عدِّل رمز النص البرمجي لإزالة المشكلة أو تجنّبها.
  3. راجِع الاختلافات الأخرى الواردة أدناه بعناية. افحص النص لتحديد ما إذا كان أي من الاختلافات المدرجة يؤثر سلوك التعليمات البرمجية. اضبط النص البرمجي لتصحيح السلوك.
  4. بعد تصحيح أيّ عدم توافق أو اختلافات أخرى تم رصدها، يمكنك البدء في تعديل الرمز البرمجي لاستخدام بنية V8 والميزات الأخرى على النحو المطلوب.
  5. بعد الانتهاء من تعديلات الرمز، اختبِر النص البرمجي بدقة التأكد من أنه يتصرف كما هو متوقع.
  6. إذا كان النص البرمجي تطبيق ويب أو إضافة منشورة، يجب إنشاء نسخة جديدة من البرنامج النصي باستخدام تعديلات V8. لإتاحة إصدار V8 للمستخدمين، يجب إعادة نشر النص البرمجي باستخدام هذا الإصدار.

حالات عدم التوافق

للأسف، سمح وقت تشغيل "برمجة تطبيقات Google" الأصلي المستند إلى Rhino بالعديد من سلوكيات ECMAScript غير القياسية. ونظرًا لأن V8 متوافق مع المعايير، فإن هذه التي تصبح متاحة بعد نقل البيانات. يؤدي عدم تصحيح هذه المشاكل إلى حدوث أخطاء أو سلوك غير صحيح للنص البرمجي بعد تفعيل وقت تشغيل V8.

توضّح الأقسام التالية كل من هذه السلوكيات والخطوات التي يجب اتخاذها. لتصحيح رمز النص البرمجي أثناء النقل إلى V8.

تجنب for each(variable in object)

تشير رسالة الأشكال البيانية for each (variable in object) تمت إضافة عبارة إلى JavaScript 1.6 وتمت إزالتها لصالح for...of.

عند نقل النص البرمجي إلى V8، تجنَّب استخدام for each (variable in object) للبيانات.

بدلاً من ذلك، يمكنك استخدام for (variable in object):

// Rhino runtime
var obj = {a: 1, b: 2, c: 3};

// Don't use 'for each' in V8
for each (var value in obj) {
  Logger.log("value = %s", value);
}
      
// V8 runtime
var obj = {a: 1, b: 2, c: 3};

for (var key in obj) {  // OK in V8
  var value = obj[key];
  Logger.log("value = %s", value);
}
      

تجنب Date.prototype.getYear()

في وقت تشغيل Rhino الأصلي، Date.prototype.getYear() تعرض السنوات المكونة من رقمين للأعوام من 1900-1999، لكن السنوات المكونة من أربعة أرقام للسنوات الأخرى وهي السلوك في JavaScript 1.2 والإصدارات السابقة.

في وقت تشغيل V8، يعرض العنصر Date.prototype.getYear() السنة مطروحًا منها 1900 بدلاً من ذلك، كما هو مطلوب بموجب معايير ECMAScript.

عند نقل النص البرمجي إلى V8، استخدِم دائمًا Date.prototype.getFullYear(), التي تعرض سنة مكونة من أربعة أرقام بغض النظر عن التاريخ.

تجنب استخدام الكلمات الرئيسية المحجوزة كأسماء

تحظر ECMAScript استخدام أنواع الكلمات الرئيسية المحجوزة في أسماء الدوال والمتغيرات. أتاح وقت تشغيل Rhino العديد من هذه الكلمات، لذلك إذا كانت التعليمة البرمجية الخاصة بك تستخدمها، فيجب عليك إعادة تسمية الدوال أو المتغيرات.

عند نقل النص البرمجي إلى V8، تجنَّب تسمية المتغيّرات أو الدوالّ باستخدام إحدى الكلمات الرئيسية المحجوزة. أعِد تسمية أي متغيّر أو دالة لتجنُّب استخدام اسم الكلمة الرئيسية. الاستخدامات الشائعة من الكلمات الرئيسية كأسماء هي class وimport وexport.

تجنَّب إعادة تعيين متغيّرات const.

في بيئة التشغيل الأصلية Rhino، يمكنك تعريف متغير باستخدام const الذي تعني أن قيمة الرمز لا تتغير أبدًا وسيتم تعيينها لاحقًا يتم تجاهلهما.

في وقت التشغيل الجديد V8، تكون الكلمة الرئيسية const متوافقة مع المعيار، ويؤدي تحديد قيمة لمتغيّر تمّت تعريفه على أنّه const إلى TypeError: Assignment to constant variable خطأ في وقت التشغيل.

عند نقل النص البرمجي إلى V8، لا تحاول إعادة تعيين قيمة متغيّر const:

// Rhino runtime
const x = 1;
x = 2;          // No error
console.log(x); // Outputs 1
      
// V8 runtime
const x = 1;
x = 2;          // Throws TypeError
console.log(x); // Never executed
      

تجنُّب القيم الحرفية بتنسيق XML وعنصر XML

هذا النمط إضافة غير عادية إلى ECMAScript إلى السماح لمشروعات "برمجة التطبيقات" باستخدام بنية XML مباشرةً.

عند نقل النص البرمجي إلى V8، تجنَّب استخدام قيم XML الحرفية المباشرة أو XML. .

بدلاً من ذلك، استخدِم XmlService تحليل XML:

// V8 runtime
var incompatibleXml1 = <container><item/></container>;             // Don't use
var incompatibleXml2 = new XML('<container><item/></container>');  // Don't use

var xml3 = XmlService.parse('<container><item/></container>');     // OK
      

عدم إنشاء دوال مكرّرة مخصّصة باستخدام __iterator__

أضافت JavaScript 1.7 ميزة للسماح بإضافة عنصر تكرار مخصّص إلى أي صفوف من خلال تعريف دالة __iterator__ في النموذج الأولي لهذه الصفوف. تمت إضافة هذه الميزة أيضًا إلى وقت تشغيل Rhino في Apps Script لتسهيل عمل المطوّرين. ومع ذلك، لم تكن هذه الميزة أبدًا جزءًا من معيار ECMA-262 وتمت إزالته في محركات JavaScript المتوافقة مع ECMAScript. النصوص البرمجية باستخدام V8 لا يمكنك استخدام بنية المكرر هذه.

عند نقل النص البرمجي إلى V8، تجنَّب استخدام دالة __iterator__ لإنشاء المكرّرات المخصّصة. بدلاً من ذلك، واستخدام مكررات ECMAScript 6.

ضع في الاعتبار إنشاء الصفيفة التالية:

// Create a sample array
var myArray = ['a', 'b', 'c'];
// Add a property to the array
myArray.foo = 'bar';

// The default behavior for an array is to return keys of all properties,
//  including 'foo'.
Logger.log("Normal for...in loop:");
for (var item in myArray) {
  Logger.log(item);            // Logs 0, 1, 2, foo
}

// To only log the array values with `for..in`, a custom iterator can be used.
      

توضح أمثلة التعليمات البرمجية التالية كيف يمكن إنشاء مكرر في بيئة تشغيل Rhino وطريقة إنشاء مكرّر بديل في بيئة تشغيل V8:

// Rhino runtime custom iterator
function ArrayIterator(array) {
  this.array = array;
  this.currentIndex = 0;
}

ArrayIterator.prototype.next = function() {
  if (this.currentIndex
      >= this.array.length) {
    throw StopIteration;
  }
  return "[" + this.currentIndex
    + "]=" + this.array[this.currentIndex++];
};

// Direct myArray to use the custom iterator
myArray.__iterator__ = function() {
  return new ArrayIterator(this);
}


Logger.log("With custom Rhino iterator:");
for (var item in myArray) {
  // Logs [0]=a, [1]=b, [2]=c
  Logger.log(item);
}
      
// V8 runtime (ECMAScript 6) custom iterator
myArray[Symbol.iterator] = function() {
  var currentIndex = 0;
  var array = this;

  return {
    next: function() {
      if (currentIndex < array.length) {
        return {
          value: "[${currentIndex}]="
            + array[currentIndex++],
          done: false};
      } else {
        return {done: true};
      }
    }
  };
}

Logger.log("With V8 custom iterator:");
// Must use for...of since
//   for...in doesn't expect an iterable.
for (var item of myArray) {
  // Logs [0]=a, [1]=b, [2]=c
  Logger.log(item);
}
      

تجنّب عبارات البحث الشرطية

لا تتيح بيئة تشغيل V8 استخدام عبارات البحث الشرطية في catch..if، لأنّها لا تتوافق مع المعايير

عند نقل النص البرمجي إلى V8، انقل أي مصطلحات شَرطية مشروطة داخل صيد السمك:

// Rhino runtime

try {
  doSomething();
} catch (e if e instanceof TypeError) {  // Don't use
  // Handle exception
}
      
// V8 runtime
try {
  doSomething();
} catch (e) {
  if (e instanceof TypeError) {
    // Handle exception
  }
}

تجنُّب استخدام Object.prototype.toSource()

كان JavaScript 1.3 يتضمّن Object.prototype.toSource()‎ method التي لم تكن أبدًا جزءًا من أي معيار ECMAScript. غير متاح في وقت تشغيل V8.

عند نقل النص البرمجي إلى الإصدار V8، أزِل أي استخدام Object.prototype.toSource() من التعليمات البرمجية.

اختلافات أخرى

بالإضافة إلى حالات عدم التوافق المذكورة أعلاه التي يمكن أن تؤدي إلى حدوث أخطاء في النصوص البرمجية، هناك بضعة اختلافات أخرى قد تؤدي إلى سلوك غير متوقّع للنصوص البرمجية أثناء التشغيل في V8 في حال عدم تصحيحها.

توضّح الأقسام التالية كيفية تحديث رمز النص البرمجي لتجنُّب حدوث هذه المشاكل مفاجآت غير متوقعة.

ضبط تنسيق التاريخ والوقت على مستوى منطقة معيّنة

Date الطريقتان toLocaleString()، toLocaleDateString(), وtoLocaleTimeString() بشكل مختلف في بيئة تشغيل V8 مقارنةً بـ Rhino.

في وحيد القرن، يكون التنسيق التلقائي هو التنسيق الطويل، وأي معلمات يتم تمريرها في يتم تجاهلها.

في وقت التشغيل V8، يكون التنسيق التلقائي هو التنسيق القصير والمَعلمات. يتم التعامل معها وفقًا لمعيار ECMA (راجع مستندات toLocaleDateString() للحصول على التفاصيل).

عند نقل النص البرمجي إلى V8، اختبِر توقعات الرمز وعدِّلها. بخصوص ناتج طرق التاريخ والوقت الخاصة باللغة:

// Rhino runtime
var event = new Date(
  Date.UTC(2012, 11, 21, 12));

// Outputs "December 21, 2012" in Rhino
console.log(event.toLocaleDateString());

// Also outputs "December 21, 2012",
//  ignoring the parameters passed in.
console.log(event.toLocaleDateString(
    'de-DE',
    { year: 'numeric',
      month: 'long',
      day: 'numeric' }));
// V8 runtime
var event = new Date(
  Date.UTC(2012, 11, 21, 12));

// Outputs "12/21/2012" in V8
console.log(event.toLocaleDateString());

// Outputs "21. Dezember 2012"
console.log(event.toLocaleDateString(
    'de-DE',
    { year: 'numeric',
      month: 'long',
      day: 'numeric' }));
      

تجنُّب استخدام Error.fileName وError.lineNumber

في وقت إيقاف V8، لا يتم اختبار لغة JavaScript Error لا يتيح الكائن fileName أو lineNumber كمعلمات الدالة الإنشائية أو خصائص الكائن.

عند نقل النص البرمجي إلى V8، أزِل أيّ اعتماد على Error.fileName وError.lineNumber.

يمكنك بدلاً من ذلك استخدام Error.prototype.stack. هذه الحزمة غير عادية أيضًا، ولكنّها متاحة في كل من Rhino وV8. تشير رسالة الأشكال البيانية يختلف تنسيق تتبُّع تسلسل استدعاء الدوال البرمجية الذي تنشئه المنصّتان قليلاً:

// Rhino runtime Error.prototype.stack
// stack trace format
at filename:92 (innerFunction)
at filename:97 (outerFunction)
// V8 runtime Error.prototype.stack
// stack trace format
Error: error message
at innerFunction (filename:92:11)
at outerFunction (filename:97:5)
      

ضبط التعامل مع كائنات التعداد المتسلسلة

في وقت تشغيل Rhino الأصلي، باستخدام JavaScript JSON.stringify() على كائن enum يؤدي إلى إرجاع {} فقط.

في V8، باستخدام نفس الطريقة على كائن enum، تتم إعادة تسمية اسم التعداد.

عند نقل النص البرمجي إلى الإصدار 8، اختبِر توقّعات الرمز البرمجي بشأن ناتج JSON.stringify() على عناصر التعداد وعدِّلها:

// Rhino runtime
var enumName =
  JSON.stringify(Charts.ChartType.BUBBLE);

// enumName evaluates to {}
// V8 runtime
var enumName =
  JSON.stringify(Charts.ChartType.BUBBLE);

// enumName evaluates to "BUBBLE"

ضبط التعامل مع المعلَمات غير المحدَّدة

في وقت تشغيل Rhino الأصلي، تمرير undefined إلى طريقة كمَعلمة أدى إلى تمرير السلسلة "undefined" إلى ذلك الطريقة.

في الإصدار 8، يؤدي تمرير undefined إلى الطرق إلى ما يعادل تمرير null.

عند نقل النص البرمجي إلى V8، اختبار وتعديل توقعات الرمز بشأن معلَمات undefined:

// Rhino runtime
SpreadsheetApp.getActiveRange()
    .setValue(undefined);

// The active range now has the string
// "undefined"  as its value.
      
// V8 runtime
SpreadsheetApp.getActiveRange()
    .setValue(undefined);

// The active range now has no content, as
// setValue(null) removes content from
// ranges.

ضبط التعامل مع this العامة

يحدِّد وقت تشغيل Rhino سياقًا خاصًا ضمنيًا للنصوص البرمجية التي تستخدمه. يتم تشغيل رمز النص البرمجي في هذا السياق الضمني، الذي يختلف عن الرمز العالمي الفعلي this وهذا يعني أن الإشارات إلى "this العالمية" في التعليمات البرمجية في الواقع تقييمه وفقًا للسياق الخاص، الذي يحتوي فقط على التعليمات البرمجية والمتغيرات المحدد في البرنامج النصي. إنّ خدمات Apps Script المضمّنة وعناصر ECMAScript غير مشمولة في هذا الاستخدام لـ this. كان هذا الموقف مشابهًا لهذا بنية JavaScript:

// Rhino runtime

// Apps Script built-in services defined here, in the actual global context.
var SpreadsheetApp = {
  openById: function() { ... }
  getActive: function() { ... }
  // etc.
};

function() {
  // Implicit special context; all your code goes here. If the global this
  // is referenced in your code, it only contains elements from this context.

  // Any global variables you defined.
  var x = 42;

  // Your script functions.
  function myFunction() {
    ...
  }
  // End of your code.
}();

في الإصدار V8، تتم إزالة السياق الخاص الضمني. المتغيّرات والدوال العمومية المحددة في النص البرمجي يتم وضعها في السياق العام، بجانب خدمات "برمجة تطبيقات Google" وأداة ECMAScript المضمّنة، مثل Math وDate.

عند نقل النص البرمجي إلى V8، اختبِر توقعات الرمز وعدِّلها. بخصوص استخدام this في سياق عالمي. في معظم الحالات، تكون الاختلافات واضحة فقط إذا كانت التعليمات البرمجية تفحص المفاتيح أو أسماء خصائص كائن this عمومي:

// Rhino runtime
var myGlobal = 5;

function myFunction() {

  // Only logs [myFunction, myGlobal];
  console.log(Object.keys(this));

  // Only logs [myFunction, myGlobal];
  console.log(
    Object.getOwnPropertyNames(this));
}





      
// V8 runtime
var myGlobal = 5;

function myFunction() {

  // Logs an array that includes the names
  // of Apps Script services
  // (CalendarApp, GmailApp, etc.) in
  // addition to myFunction and myGlobal.
  console.log(Object.keys(this));

  // Logs an array that includes the same
  // values as above, and also includes
  // ECMAScript built-ins like Math, Date,
  // and Object.
  console.log(
    Object.getOwnPropertyNames(this));
}

ضبط التعامل مع instanceof في المكتبات

يمكن أن يؤدي استخدام instanceof في مكتبة على عنصر يتم تمريره كمَعلمة في دالّة من مشروع آخر إلى تقديم نتائج سلبية خاطئة. في وقت تشغيل V8، تشغيل المشروع ومكتباته في سياقات تنفيذ مختلفة وبالتالي عوالم مختلفة وسلاسل نماذج أوّلية.

يُرجى العلم أنّ هذا يحدث فقط إذا كانت مكتبتك تستخدم instanceof على عنصر لم يتم إنشاؤه في مشروعك. استخدامه على كائن يتم إنشاؤه في مشروعك، سواء في البرنامج النصي ذاته أو نص مختلف داخل المشروع كما هو متوقع.

إذا كان أحد المشاريع قيد التشغيل على الإصدار V8 يستخدم النص البرمجي كمكتبة، تحقَّق مما إذا كان يستخدم النص البرمجي instanceof على معلَمة سيتم تمريرها من مشروع آخر. ضبط استخدام "instanceof" واستخدام بدائل أخرى مجدية وفقًا لاستخدامك الطلب

يمكن أن يكون أحد البدائل a instanceof b هو استخدام أداة الإنشاء a في الحالات التي لا تحتاج فيها إلى البحث في سلسلة النماذج الأولية بأكملها والتحقق فقط من أداة الإنشاء. الاستخدام: a.constructor.name == "b"

ضع في اعتبارك المشروع "أ" والمشروع "ب" حيث يستخدم المشروع "ب" المشروع "ب" كمكتبة.

//Rhino runtime

//Project A

function caller() {
   var date = new Date();
   // Returns true
   return B.callee(date);
}

//Project B

function callee(date) {
   // Returns true
   return(date instanceof Date);
}

      
//V8 runtime

//Project A

function caller() {
   var date = new Date();
   // Returns false
   return B.callee(date);
}

//Project B

function callee(date) {
   // Incorrectly returns false
   return(date instanceof Date);
   // Consider using return (date.constructor.name ==
   // Date) instead.
   // return (date.constructor.name == Date) -> Returns
   // true
}

يمكن أن يكون البديل الآخر هو تقديم دالة تتحقق من instanceof في المشروع الرئيسي وتمرير الدالة بالإضافة إلى معلمات أخرى عند استدعاء دالة مكتبة. الدالة التي تم تمريرها بعد ذلك للتحقق من instanceof داخل المكتبة.

//V8 runtime

//Project A

function caller() {
   var date = new Date();
   // Returns True
   return B.callee(date, date => date instanceof Date);
}

//Project B

function callee(date, checkInstanceOf) {
  // Returns True
  return checkInstanceOf(date);
}
      

ضبط تمرير الموارد غير المشتركة إلى المكتبات

يختلف عمل تمرير مورد غير مشترَك من النص البرمجي الرئيسي إلى مكتبة في وقت تشغيل V8.

في وقت تشغيل Rhino، لن تنجح عملية تمرير مورد غير مشترَك. تستخدم المكتبة موردها الخاص بدلاً من ذلك.

وفي وقت التشغيل V8، ينجح تمرير مورد غير مشترك إلى المكتبة. تستخدم المكتبة المورد غير المشترك الذي تم تمريره.

لا تمرّر الموارد غير المشتركة كمعلَمات للدوال. يُرجى تعريف الموارد غير المشتركة دائمًا باستخدام النص البرمجي نفسه الذي تستخدمه.

ضع في اعتبارك المشروع "أ" والمشروع "ب" حيث يستخدم المشروع "ب" المشروع "ب" كمكتبة. في هذا المثال، PropertiesService هو مورد غير مشترك.

// Rhino runtime
// Project A
function testPassingNonSharedProperties() {
  PropertiesService.getScriptProperties()
      .setProperty('project', 'Project-A');
  B.setScriptProperties();
  // Prints: Project-B
  Logger.log(B.getScriptProperties(
      PropertiesService, 'project'));
}

//Project B function setScriptProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-B'); } function getScriptProperties( propertiesService, key) { return propertiesService.getScriptProperties() .getProperty(key); }

// V8 runtime
// Project A
function testPassingNonSharedProperties() {
  PropertiesService.getScriptProperties()
      .setProperty('project', 'Project-A');
  B.setScriptProperties();
  // Prints: Project-A
  Logger.log(B.getScriptProperties(
      PropertiesService, 'project'));
}

// Project B function setProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-B'); } function getScriptProperties( propertiesService, key) { return propertiesService.getScriptProperties() .getProperty(key); }

تعديل إذن الوصول إلى النصوص البرمجية المستقلة

بالنسبة إلى النصوص البرمجية المستقلة التي تعمل على بيئة تشغيل V8، عليك توفير ما لا يقل عن المستخدمين الإذن بالاطّلاع على النص البرمجي لكي تعمل مشغلات النص البرمجي بشكل صحيح.