سيتم إيقاف وقت تشغيل Rhino نهائيًا في 31 يناير 2026 أو بعده. إذا كان لديك نص برمجي حالي يستخدم وقت تشغيل Rhino، عليك نقله إلى V8.
في كثير من الأحيان، يكون الشرط الأساسي الوحيد لإضافة بنية V8 وميزاتها إلى نص برمجي هو تفعيل وقت تشغيل V8. ومع ذلك، هناك مجموعة صغيرة من حالات عدم التوافق والاختلافات الأخرى التي يمكن أن تؤدي إلى تعذُّر تنفيذ البرنامج النصي أو سلوكه بشكل غير متوقّع في وقت تشغيل V8. عند نقل نص برمجي لاستخدام V8، عليك البحث في مشروع النص البرمجي عن هذه المشاكل وتصحيح أي مشاكل تجدها.
إجراء نقل البيانات إلى V8
لنقل نص برمجي إلى V8، اتّبِع الإجراء التالي:
- فعِّل وقت التشغيل V8
للنص البرمجي. يمكن التحقّق من
runtimeVersion
باستخدام بيان مشروع "برمجة التطبيقات". - يُرجى مراجعة حالات عدم التوافق التالية بعناية. افحص النص البرمجي لتحديد ما إذا كان يتضمّن أيًا من حالات عدم التوافق. وإذا كان يتضمّن حالة واحدة أو أكثر، عدِّل رمز النص البرمجي لإزالة المشكلة أو تجنُّبها.
- يُرجى مراجعة الاختلافات الأخرى التالية بعناية. افحص النص البرمجي لتحديد ما إذا كان أي من الاختلافات المدرَجة يؤثر في سلوك الرمز. عدِّل النص البرمجي لتصحيح السلوك.
- بعد تصحيح أي حالات عدم توافق أو اختلافات أخرى تم رصدها، يمكنك البدء في تعديل الرمز لاستخدام بنية V8 والميزات الأخرى.
- بعد الانتهاء من تعديل الرمز، اختبِر النص البرمجي بدقة للتأكّد من أنّه يعمل على النحو المتوقّع.
- إذا كان النص البرمجي تطبيق ويب أو إضافة تم نشرها، عليك إنشاء إصدار جديد من النص البرمجي مع تعديلات V8، وتوجيه عملية النشر إلى الإصدار الذي تم إنشاؤه حديثًا. لإتاحة إصدار V8 للمستخدمين، عليك إعادة نشر النص البرمجي باستخدام هذا الإصدار.
- إذا تم استخدام النص البرمجي كمكتبة، عليك إنشاء إصدار جديد من عملية نشر النص البرمجي. أبلِغ جميع البرامج النصية والمستخدمين الذين يستخدمون مكتبتك بهذا الإصدار الجديد، واطلب منهم التحديث إلى الإصدار المتوافق مع V8. تأكَّد من أنّ أي إصدارات قديمة من مكتبتك تستند إلى Rhino لم يعُد يتم استخدامها أو يمكن الوصول إليها.
- تأكَّد من عدم استمرار تشغيل أي مثيلات من النص البرمجي على وقت تشغيل 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 في "برمجة تطبيقات 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. |
توضّح أمثلة الرموز البرمجية التالية كيفية إنشاء مكرّر في بيئة تشغيل 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، تتم إزالة السياق الخاص الضمني. يتم وضع المتغيرات والدوال العمومية المحدّدة في النص البرمجي في سياق عمومي، وذلك بجانب خدمات "برمجة تطبيقات Google" المضمّنة وعناصر 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')); } |
// V8 runtime // Project A function testPassingNonSharedProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-A'); B.setScriptProperties(); // Prints: Project-A Logger.log(B.getScriptProperties( PropertiesService, 'project')); } |
تعديل إذن الوصول إلى النصوص البرمجية المستقلة
بالنسبة إلى النصوص البرمجية المستقلة التي تعمل على وقت تشغيل V8، عليك منح المستخدمين إذن الوصول إلى النص البرمجي على الأقل، وذلك لكي تعمل مشغّلات النص البرمجي بشكلٍ سليم.