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

سيتم إيقاف وقت تشغيل Rhino نهائيًا في 31 يناير 2026 أو بعده. إذا كان لديك نص برمجي حالي يستخدم وقت تشغيل Rhino، عليك نقله إلى V8.

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

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

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

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

العروض غير المتوافقة

للأسف، كان وقت التشغيل الأصلي المستند إلى Rhino في "برمجة تطبيقات Google" يسمح بعدة سلوكيات غير عادية في ECMAScript. بما أنّ الإصدار 8 متوافق مع المعايير، لن تكون هذه السلوكيات متاحة بعد نقل البيانات. سيؤدي عدم تصحيح هذه المشاكل إلى حدوث أخطاء أو تعذُّر عمل النص البرمجي بشكل صحيح بعد تفعيل وقت تشغيل 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.

عند نقل النص البرمجي إلى الإصدار 8، استخدِم دائمًا 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 في &quot;برمجة تطبيقات Google&quot; لتوفير الراحة للمطوّرين. ومع ذلك، لم تكن هذه الميزة جزءًا من معيار 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);
}
      

تجنُّب عبارات catch الشرطية

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

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

// 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() التي لم تكن جزءًا من أي معيار ECMAScript. ولا تتوفّر في وقت تشغيل V8.

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

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

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

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

تعديل تنسيق التاريخ والوقت الخاص باللغة

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

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

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

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

// 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. هذه السلسلة غير عادية أيضًا، ولكنّها متوافقة مع 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() على كائن تعداد إلى عرض سوى {}.

في الإصدار 8، يؤدي استخدام الطريقة نفسها على عنصر تعداد إلى عرض اسم التعداد.

عند نقل النص البرمجي إلى V8، اختبِر وعدِّل توقعات الرمز البرمجي بشأن ناتج 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" إلى تلك الطريقة.

في V8، يكون تمرير 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 العام" في الرمز البرمجي يتم تقييمها في الواقع إلى السياق الخاص الذي يحتوي فقط على الرمز البرمجي والمتغيرات المحددة في النص البرمجي. يُستثنى من هذا الاستخدام لـ this خدمات Apps Script المضمّنة وعناصر ECMAScript. كان هذا الموقف مشابهًا لبنية 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.
}();

في الإصدار 8، تتم إزالة السياق الخاص الضمني. يتم وضع المتغيرات والدوال العمومية المحدّدة في النص البرمجي في سياق عمومي، وذلك بجانب خدمات &quot;برمجة تطبيقات Google&quot; المضمّنة وعناصر ECMAScript المضمّنة، مثل Math وDate.

عند نقل النص البرمجي إلى الإصدار 8، اختبِر توقّعات الرمز البرمجي وعدِّلها في ما يتعلّق باستخدام 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، عليك منح المستخدمين إذن الوصول إلى النص البرمجي على الأقل، وذلك لكي تعمل مشغّلات النص البرمجي بشكلٍ سليم.