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

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.

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

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

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

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

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

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

غير متوافق

سمح وقت تشغيل "برمجة التطبيقات" المستند إلى 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__

أضاف الإصدار 1.7 من JavaScript ميزة للسماح بإضافة مكرّر مخصّص إلى أي عيّنات أخرى من خلال الإعلان عن دالة __iterator__ في هذا النموذج الأوليّ، وتمّت إضافة ذلك أيضًا إلى وقت تشغيل Rhino في "برمجة تطبيقات Google". ومع ذلك، لم تكن هذه الميزة جزءًا من معيار 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.
      

توضّح أمثلة الرموز التالية كيف يمكن إنشاء مكرّر في وقت تشغيل وحيد القرن، وكيفية إنشاء مكرّر بديل في وقت تشغيل 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()

تضمّن الإصدار 1.3 من JavaScript طريقة Object.prototype.toSource() لم تكن مطلقًا جزءًا من أي معيار ECMAScript. ولا يتم توفيره في وقت تشغيل V8.

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

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

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

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

ضبط التنسيق حسب التاريخ والمنطقة حسب اللغة

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

تنسيق 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() على كائن تعداد إلى عرض {} فقط.

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

عند نقل نصك البرمجي إلى 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;;;;;;; ;;; ;;;;;;تت;الكم,;;;;; ;;، يتم استبعاد خدمات برمجة التطبيقات وكائنات 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.
}();

وفي الإصدار الثامن، تتم إزالة السياق الخاص الضمني. ويتم وضع المتغيرات والوظائف العامة المحدَّدة في النص البرمجي في السياق العالمي، بجانب خدمات "برمجة تطبيقات 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، يجب أن توفّر للمستخدمين إمكانية وصول إلى العرض على الأقل لكي تتمكّن النصوص البرمجية من العمل بشكل صحيح.