Runtime Rhino akan ditolak mulai 31 Januari 2026. Jika Anda memiliki skrip yang ada menggunakan runtime Rhino, Anda harus memigrasikan skrip ke V8.
Sering kali satu-satunya prasyarat untuk menambahkan sintaksis dan fitur V8 ke skrip adalah mengaktifkan runtime V8. Namun, ada sedikit ketidakcocokan dan perbedaan lainnya yang dapat menyebabkan skrip gagal atau berperilaku tidak terduga di runtime V8. Saat memigrasikan skrip untuk menggunakan V8, Anda harus menelusuri project skrip untuk menemukan masalah ini dan memperbaiki masalah yang ditemukan.
Prosedur migrasi V8
Untuk memigrasikan skrip ke V8, ikuti prosedur berikut:
- Aktifkan runtime V8
untuk skrip.
runtimeVersion
dapat diperiksa menggunakan manifest untuk project Apps Script. - Tinjau inkompatibilitas berikut dengan cermat. Periksa skrip Anda untuk menentukan apakah ada ketidakcocokan; jika ada satu atau beberapa ketidakcocokan, sesuaikan kode skrip Anda untuk menghapus atau menghindari masalah tersebut.
- Tinjau dengan cermat perbedaan lainnya berikut. Periksa skrip Anda untuk menentukan apakah ada perbedaan yang tercantum yang memengaruhi perilaku kode Anda. Sesuaikan skrip Anda untuk memperbaiki perilaku.
- Setelah memperbaiki ketidakcocokan atau perbedaan lainnya yang ditemukan, Anda dapat mulai memperbarui kode untuk menggunakan sintaksis V8 dan fitur lainnya.
- Setelah menyelesaikan penyesuaian kode, uji skrip Anda secara menyeluruh untuk memastikan skrip berfungsi seperti yang diharapkan.
- Jika skrip Anda adalah aplikasi web atau add-on yang dipublikasikan, Anda harus membuat versi baru skrip dengan penyesuaian V8, dan mengarahkan deployment ke versi yang baru dibuat. Agar versi V8 tersedia bagi pengguna, Anda harus memublikasikan ulang skrip dengan versi ini.
- Jika skrip Anda digunakan sebagai library, buat deployment baru yang diberi versi untuk skrip Anda. Memberitahukan versi baru ini ke semua skrip dan pengguna yang menggunakan library Anda, serta menginstruksikan mereka untuk mengupdate ke versi yang kompatibel dengan V8. Verifikasi bahwa versi library berbasis Rhino yang lebih lama tidak lagi digunakan atau dapat diakses.
- Pastikan tidak ada instance skrip Anda yang masih beroperasi di runtime Rhino lama. Pastikan semua deployment dikaitkan dengan versi yang ada di V8. Arsipkan deployment lama. Tinjau semua versi dan hapus versi yang tidak menggunakan V8 Runtime.
Ketidaksesuaian
Runtime Apps Script berbasis Rhino asli sayangnya mengizinkan beberapa perilaku ECMAScript non-standar. Karena V8 sesuai dengan standar, perilaku ini tidak didukung setelah migrasi. Jika masalah ini tidak diperbaiki, akan terjadi error atau perilaku skrip yang rusak setelah runtime V8 diaktifkan.
Bagian berikut menjelaskan setiap perilaku ini dan langkah-langkah yang harus Anda lakukan untuk memperbaiki kode skrip selama migrasi ke V8.
Menghindarinfor each(variable in object)
Pernyataan
for each (variable in object)
ditambahkan ke JavaScript 1.6, dan dihapus untuk mendukung for...of
.
Saat memigrasikan skrip ke V8, hindari penggunaan pernyataan for each (variable in object)
.
Sebagai gantinya, gunakan 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); } |
MenghindarinDate.prototype.getYear()
Di runtime Rhino asli,
Date.prototype.getYear()
menampilkan tahun dua digit untuk tahun 1900-1999, tetapi tahun empat digit untuk tanggal lainnya, yang merupakan perilaku di JavaScript 1.2 dan yang lebih lama.
Di runtime V8,
Date.prototype.getYear()
menampilkan tahun dikurangi 1900, bukan seperti yang diperlukan oleh
standar ECMAScript.
Saat memigrasikan skrip ke V8, selalu gunakan
Date.prototype.getFullYear()
,
yang menampilkan tahun empat digit terlepas dari tanggalnya.
Hindari penggunaan kata kunci yang dicadangkan sebagai nama
ECMAScript melarang penggunaan kata kunci yang dicadangkan tertentu dalam nama fungsi dan variabel. Runtime Rhino mengizinkan banyak kata ini, jadi jika kode Anda menggunakannya, Anda harus mengganti nama fungsi atau variabel.
Saat memigrasikan skrip ke V8, hindari penamaan variabel atau fungsi
menggunakan salah satu
kata kunci yang dicadangkan.
Ganti nama variabel atau fungsi apa pun untuk menghindari penggunaan nama kata kunci. Penggunaan umum
kata kunci sebagai nama adalah class
, import
, dan export
.
Hindari penetapan ulang variabel const
Di runtime Rhino asli, Anda dapat mendeklarasikan variabel menggunakan const
yang berarti nilai simbol tidak pernah berubah dan penetapan ke simbol di masa mendatang akan diabaikan.
Di runtime V8 baru, kata kunci const
sesuai dengan standar dan penetapan
ke variabel yang dideklarasikan sebagai const
akan menghasilkan
error runtime TypeError: Assignment to constant variable
.
Saat memigrasikan skrip ke V8, jangan mencoba menetapkan ulang nilai
variabel 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 |
Hindari literal XML dan objek XML
Ekstensi non-standar ke ECMAScript ini memungkinkan project Apps Script menggunakan sintaksis XML secara langsung.
Saat memigrasikan skrip ke V8, hindari penggunaan literal XML langsung atau objek XML.
Sebagai gantinya, gunakan XmlService untuk mengurai 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 |
Jangan membuat fungsi iterator kustom menggunakan __iterator__
JavaScript 1.7 menambahkan fitur untuk memungkinkan penambahan iterator kustom ke class apa pun dengan mendeklarasikan fungsi __iterator__
dalam prototipe class tersebut; fitur ini juga ditambahkan ke runtime Rhino Apps Script sebagai kemudahan bagi developer. Namun,
fitur ini tidak pernah menjadi bagian dari
standar ECMA-262
dan dihapus di mesin JavaScript yang kompatibel dengan ECMAScript. Skrip yang menggunakan V8 tidak dapat menggunakan konstruksi iterator ini.
Saat memigrasikan skrip ke V8, hindari fungsi __iterator__
untuk membuat iterator kustom. Sebagai gantinya,
gunakan iterator ECMAScript 6.
Pertimbangkan konstruksi array berikut:
// 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. |
Contoh kode berikut menunjukkan cara membuat iterator di runtime Rhino, dan cara membuat iterator pengganti di runtime 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); } |
Hindari klausa penangkapan bersyarat
Runtime V8 tidak mendukung klausa catch bersyarat catch..if
, karena tidak sesuai dengan standar.
Saat memigrasikan skrip ke V8, pindahkan kondisi catch apa pun di dalam isi 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 } } |
Hindari penggunaan Object.prototype.toSource()
JavaScript 1.3 berisi metode Object.prototype.toSource() yang tidak pernah menjadi bagian dari standar ECMAScript. Fitur ini tidak didukung di runtime V8.
Saat memigrasikan skrip ke V8, hapus semua penggunaan Object.prototype.toSource() dari kode Anda.
Perbedaan lainnya
Selain ketidakcocokan sebelumnya yang dapat menyebabkan kegagalan skrip, ada beberapa perbedaan lain yang, jika tidak diperbaiki, dapat menyebabkan perilaku skrip runtime V8 yang tidak terduga.
Bagian berikut menjelaskan cara memperbarui kode skrip Anda untuk menghindari kejutan yang tidak terduga ini.
Menyesuaikan pemformatan tanggal dan waktu khusus lokalitas
Metode Date
,
toLocaleString()
,
toLocaleDateString()
,
dan toLocaleTimeString()
berperilaku berbeda di runtime V8 dibandingkan dengan Rhino.
Di Rhino, format defaultnya adalah format panjang, dan parameter apa pun yang diteruskan akan diabaikan.
Di runtime V8, format defaultnya adalah format pendek dan parameter yang
diteruskan ditangani sesuai dengan standar ECMA (lihat
dokumentasi toLocaleDateString()
untuk mengetahui detailnya).
Saat memigrasikan skrip ke V8, uji dan sesuaikan ekspektasi kode Anda terkait output metode tanggal dan waktu khusus lokalitas:
// 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' })); |
Hindari penggunaan Error.fileName
dan Error.lineNumber
Di runtime V8, objek JavaScript
Error
standar tidak mendukung fileName
atau lineNumber
sebagai parameter konstruktor
atau properti objek.
Saat memigrasikan skrip Anda ke V8,
hapus semua dependensi pada Error.fileName
dan Error.lineNumber
.
Alternatifnya adalah menggunakan
Error.prototype.stack
.
Stack ini juga tidak standar, tetapi didukung di V8. Format stack trace yang dihasilkan oleh kedua platform sedikit berbeda:
// 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) |
Menyesuaikan penanganan objek enum yang di-string
Di runtime Rhino asli, penggunaan metode
JSON.stringify()
JavaScript pada objek enum hanya menampilkan {}
.
Di V8, menggunakan metode yang sama pada objek enum akan menampilkan nama enum.
Saat memigrasikan skrip ke V8,
uji dan sesuaikan ekspektasi kode Anda terkait output
JSON.stringify()
pada objek enum:
// 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" |
Menyesuaikan penanganan parameter yang tidak ditentukan
Di runtime Rhino asli, meneruskan undefined
ke metode sebagai parameter akan menghasilkan penerusan string "undefined"
ke metode tersebut.
Di V8, meneruskan undefined
ke metode sama dengan meneruskan null
.
Saat memigrasikan skrip ke V8,
uji dan sesuaikan ekspektasi kode Anda terkait parameter 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. |
Menyesuaikan penanganan this
global
Runtime Rhino menentukan konteks khusus implisit untuk skrip yang menggunakannya.
Kode skrip berjalan dalam konteks implisit ini, berbeda dengan global
this
yang sebenarnya. Artinya, referensi ke "this
global" dalam kode sebenarnya dievaluasi ke konteks khusus, yang hanya berisi kode dan variabel yang ditentukan dalam skrip. Layanan Apps Script bawaan dan
objek ECMAScript
dikecualikan dari penggunaan this
ini. Situasi ini mirip dengan struktur JavaScript berikut:
// 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. }(); |
Di V8, konteks khusus implisit dihapus. Variabel dan fungsi global yang ditentukan dalam skrip ditempatkan dalam konteks global, di samping layanan Apps Script bawaan dan bawaan ECMAScript seperti Math
dan Date
.
Saat memigrasikan skrip ke V8, uji dan sesuaikan ekspektasi kode Anda
mengenai penggunaan this
dalam konteks global. Dalam sebagian besar kasus, perbedaan hanya terlihat jika kode Anda memeriksa kunci atau nama properti objek this
global:
// 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)); } |
Menyesuaikan penanganan instanceof
di library
Menggunakan instanceof
di library pada objek yang diteruskan sebagai parameter dalam
fungsi dari project lain dapat memberikan negatif palsu. Dalam runtime V8, project dan library-nya dijalankan dalam konteks eksekusi yang berbeda sehingga memiliki global dan rantai prototipe yang berbeda.
Perhatikan bahwa hal ini hanya terjadi jika library Anda menggunakan instanceof
pada objek
yang tidak dibuat di project Anda. Menggunakannya pada objek yang dibuat di
project Anda, baik dalam skrip yang sama atau berbeda di dalam project Anda,
akan berfungsi seperti yang diharapkan.
Jika project yang berjalan di V8 menggunakan skrip Anda sebagai library, periksa apakah skrip Anda menggunakan instanceof
pada parameter yang akan diteruskan dari project lain. Sesuaikan
penggunaan instanceof
dan gunakan alternatif lain yang memungkinkan sesuai dengan kasus penggunaan Anda.
Salah satu alternatif untuk a instanceof b
adalah menggunakan konstruktor a
dalam
kasus saat Anda tidak perlu menelusuri seluruh rantai prototipe dan hanya perlu memeriksa
konstruktor.
Penggunaan: a.constructor.name == "b"
Pertimbangkan Project A dan Project B, dengan Project A menggunakan Project B sebagai library.
//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 } |
Alternatif lain adalah dengan memperkenalkan fungsi yang memeriksa instanceof
di project utama
dan meneruskan fungsi selain parameter lain saat memanggil fungsi library. Fungsi yang diteruskan
kemudian dapat digunakan untuk memeriksa instanceof
di dalam library.
//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); } |
Menyesuaikan penerusan resource yang tidak dibagikan ke pustaka
Penerusan resource non-bersama dari skrip utama ke library berfungsi secara berbeda di runtime V8.
Di runtime Rhino, meneruskan resource yang tidak dibagikan tidak akan berfungsi. Library menggunakan resource-nya sendiri.
Di runtime V8, meneruskan resource yang tidak dibagikan ke library akan berfungsi. Library menggunakan resource non-bersama yang diteruskan.
Jangan teruskan resource yang tidak dibagikan sebagai parameter fungsi. Selalu deklarasikan resource yang tidak dibagikan dalam skrip yang sama yang menggunakannya.
Pertimbangkan Project A dan Project B, dengan Project A menggunakan Project B sebagai library. Dalam contoh ini, PropertiesService
adalah resource yang tidak dibagikan.
// 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')); } |
Memperbarui akses ke skrip mandiri
Untuk skrip mandiri yang berjalan di runtime V8, Anda harus memberi pengguna setidaknya akses lihat ke skrip agar pemicu skrip berfungsi dengan baik.