Jika Anda sudah memiliki skrip yang menggunakan runtime Rhino dan ingin menggunakan dari fitur dan sintaksis V8, Anda harus memigrasikan skrip ke V8.
Sebagian besar skrip yang ditulis menggunakan runtime Rhino dapat beroperasi menggunakan runtime V8 tanpa penyesuaian. Sering kali satu-satunya prasyarat untuk menambahkan sintaks V8 dan fitur ke skrip adalah mengaktifkan runtime V8.
Namun, ada sekumpulan kecil ketidaksesuaian dan perbedaan lain yang dapat menghasilkan skrip gagal atau berperilaku tiba-tiba setelah mengaktifkan runtime V8. Saat Anda bermigrasi skrip untuk menggunakan V8, Anda harus mencari masalah skrip di proyek skrip ini dan memperbaiki apa pun yang Anda temukan.
Prosedur migrasi V8
Untuk memigrasikan skrip ke V8, ikuti prosedur ini:
- Mengaktifkan runtime V8 untuk skrip.
- Tinjau inkompatibilitas dengan cermat yang tercantum di bawah ini. Periksa skrip Anda untuk menentukan apakah ada terdapat inkompatibilitas; jika ada satu atau lebih inkompatibilitas yang muncul, menyesuaikan kode skrip Anda untuk menghapus atau menghindari masalah.
- Tinjau perbedaan lain yang tercantum di bawah ini dengan cermat. Periksa skrip Anda untuk menentukan apakah salah satu perbedaan yang tercantum memengaruhi perilaku kode Anda. Sesuaikan skrip Anda untuk memperbaiki perilaku tersebut.
- Setelah Anda memperbaiki inkompatibilitas yang ditemukan atau Anda dapat mulai memperbarui kode untuk menggunakan Sintaksis V8 dan fitur lainnya sesuai keinginan.
- Setelah menyelesaikan penyesuaian kode, uji skrip Anda secara menyeluruh untuk membuat memastikan perilakunya sesuai dengan yang diharapkan.
- Jika skrip Anda adalah aplikasi web atau add-on yang dipublikasikan, Anda harus buat versi baru skrip dengan penyesuaian V8. Untuk membuat versi V8 tersedia bagi pengguna, Anda harus menerbitkan ulang skrip dengan versi ini.
Inkompatibilitas
Sayangnya, runtime Apps Script berbasis Rhino yang asli mengizinkan beberapa perilaku ECMAScript non-standar. Karena V8 sesuai standar, tidak didukung setelah migrasi. Kegagalan untuk memperbaiki masalah ini mengakibatkan error atau perilaku skrip yang rusak setelah runtime V8 diaktifkan.
Bagian berikut menjelaskan masing-masing perilaku tersebut dan langkah-langkah yang harus Anda ambil untuk memperbaiki kode skrip Anda selama migrasi ke V8.
Menghindarinfor each(variable in object)
Tujuan
for each (variable in object)
ditambahkan ke JavaScript 1.6, dan dihapus demi for...of
.
Saat memigrasikan skrip ke V8, hindari penggunaan for each (variable in object)
pernyataan pribadi.
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()
Dalam runtime Rhino asli,
Date.prototype.getYear()
mengembalikan tahun dalam dua digit dari 1900-1999, tetapi
tanggal, yang merupakan perilaku
di JavaScript 1.2 dan sebelumnya.
Dalam runtime V8,
Date.prototype.getYear()
mengembalikan tahun dikurangi 1900,
bukannya seperti yang disyaratkan oleh
sesuai standar ECMAScript.
Saat memigrasikan skrip ke V8, selalu gunakan
Date.prototype.getFullYear()
,
yang menghasilkan empat digit tahun
tanpa memandang tanggal.
Hindari penggunaan kata kunci yang dicadangkan sebagai nama
ECMAScript melarang penggunaan kata kunci yang dicadangkan 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 menetapkan ulang const
variabel
Dalam runtime Rhino asli, Anda dapat mendeklarasikan variabel menggunakan const
yang
berarti nilai simbol tidak pernah berubah dan penugasan berikutnya ke
akan diabaikan.
Di runtime V8 yang baru, kata kunci const
sesuai standar dan menetapkan
ke variabel yang dideklarasikan sebagai const
menghasilkan
Error runtime TypeError: Assignment to constant variable
.
Saat memigrasikan skrip ke V8, jangan coba 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
Ini ekstensi non-standar ke ECMAScript memungkinkan project Apps Script menggunakan sintaksis XML secara langsung.
Saat memigrasikan skrip ke V8, hindari penggunaan literal XML langsung atau XML objek.
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 membangun fungsi iterator kustom menggunakan __iterator__
JavaScript 1.7 menambahkan fitur yang memungkinkan penambahan iterator kustom ke semua class
s dengan mendeklarasikan fungsi __iterator__
dalam prototipe class tersebut; ini adalah
juga ditambahkan ke runtime Rhino Apps Script sebagai kenyamanan developer. Namun,
fitur ini tidak pernah menjadi bagian dari
Standar ECMA-262
dan dihapus di mesin JavaScript yang sesuai dengan ECMAScript. Skrip yang menggunakan V8
tidak dapat menggunakan
konstruksi iterator ini.
Saat memigrasikan skrip ke V8, hindari fungsi __iterator__
untuk membangun
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 bagaimana iterator dapat dibuat dalam Runtime Rhino, dan cara membuat iterator pengganti dalam 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); } |
Menghindari klausa tangkapan bersyarat
Runtime V8 tidak mendukung klausa tangkapan bersyarat catch..if
, karena
tidak sesuai standar.
Saat memigrasikan skrip ke V8, pindahkan kondisional catch di dalam catch body:
// 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 menggunakan Object.prototype.toSource()
JavaScript 1.3 berisi Object.prototype.toSource() yang tidak pernah menjadi bagian dari standar ECMAScript apa pun. Hal ini tidak didukung di runtime V8.
Saat memigrasikan skrip ke V8, hapus semua penggunaan Object.prototype.toSource() dari kode Anda.
Perbedaan lainnya
Selain inkompatibilitas di atas yang dapat menyebabkan kegagalan skrip, ada ada beberapa perbedaan lain yang, jika tidak dikoreksi, dapat menghasilkan V8 perilaku skrip runtime.
Bagian berikut menjelaskan cara memperbarui kode skrip untuk menghindari masalah ini kejutan tak terduga.
Menyesuaikan format tanggal dan waktu khusus lokalitas
Date
metode toLocaleString()
,
toLocaleDateString()
,
dan toLocaleTimeString()
berperilaku
berbeda dalam {i>runtime<i} V8 dibandingkan dengan Rhino.
Di Rhino, format defaultnya adalah format panjang, dan parameter apa pun yang diteruskan diabaikan.
Dalam runtime V8, format defaultnya adalah format singkat dan parameter
yang diteruskan akan ditangani sesuai
standar ECMA (lihat dokumen
Dokumentasi toLocaleDateString()
untuk melihat 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 menggunakan Error.fileName
dan Error.lineNumber
Dalam untime V8, JavaScript standar
Error
objek tidak mendukung fileName
atau lineNumber
sebagai parameter konstruktor
atau properti objek.
Saat memigrasikan skrip ke V8,
menghapus ketergantungan apa pun pada Error.fileName
dan Error.lineNumber
.
Alternatifnya adalah dengan menggunakan
Error.prototype.stack
Stack ini juga non-standar, tetapi didukung di Rhino dan V8. Tujuan
format pelacakan tumpukan yang dihasilkan oleh kedua platform tersebut 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 dengan string
Dalam runtime Rhino asli, menggunakan JavaScript
JSON.stringify()
pada objek enum hanya menampilkan {}
.
Di V8, menggunakan metode yang sama pada objek enum akan mengembalikan nama enum.
Saat memigrasikan skrip ke V8,
dan menyesuaikan harapan kode Anda mengenai {i>output<i} dari
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
Dalam runtime Rhino asli, meneruskan undefined
ke metode sebagai parameter
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 mendefinisikan konteks khusus implisit untuk skrip yang menggunakannya.
Kode skrip berjalan dalam konteks implisit ini, berbeda dari yang sebenarnya
this
. Ini berarti referensi ke "this
global" dalam kode
mengevaluasi ke konteks khusus, yang hanya berisi kode dan variabel
yang didefinisikan dalam skrip. Layanan Apps Script dan objek ECMAScript bawaan
dikecualikan dari penggunaan this
ini. Situasi ini serupa dengan
Struktur 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. }(); |
Di V8, konteks khusus implisit dihapus. Fungsi dan variabel global
yang didefinisikan dalam skrip ditempatkan dalam konteks global, di samping
Layanan Apps Script dan fitur bawaan ECMAScript seperti Math
dan Date
.
Saat memigrasikan skrip ke V8, uji dan sesuaikan ekspektasi kode Anda
mengenai penggunaan this
dalam konteks global. Dalam kebanyakan kasus, perbedaan
hanya terlihat jika kode Anda memeriksa kunci atau nama properti dari
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
dari proyek lain dapat
memberikan negatif palsu. Dalam {i>runtime<i} V8, sebuah
dan library-nya dijalankan dalam konteks eksekusi yang berbeda,
global dan rantai prototipe yang berbeda.
Perhatikan bahwa hal ini hanya berlaku jika library Anda menggunakan instanceof
pada objek
yang tidak dibuat dalam project Anda. Menggunakannya pada objek yang dibuat di
dalam proyek Anda, baik dalam skrip
yang sama atau berbeda di dalam proyek,
akan berfungsi seperti yang diharapkan.
Jika project yang berjalan di V8 menggunakan skrip Anda sebagai library, periksa apakah
menggunakan instanceof
pada parameter yang akan diteruskan dari project lain. Sesuaikan
penggunaan instanceof
dan gunakan alternatif lain yang mungkin sesuai penggunaan Anda
ini masalahnya.
Salah satu alternatif untuk a instanceof b
dapat menggunakan konstruktor a
di
kasus di mana Anda tidak perlu mencari
seluruh rantai prototipe dan hanya memeriksa
konstruktor.
Penggunaan: a.constructor.name == "b"
Pertimbangkan Proyek A dan Proyek B di mana Proyek A menggunakan Proyek B sebagai pustaka.
//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 di samping parameter lain saat memanggil fungsi library. Fungsi yang diteruskan
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 non-bersama ke library
Meneruskan resource yang non-dibagikan dari skrip utama ke library akan berfungsi secara berbeda di runtime V8.
Dalam runtime Rhino, meneruskan resource non-bersama tidak akan berfungsi. Sebagai gantinya, library akan menggunakan resource-nya sendiri.
Di runtime V8, meneruskan resource non-bersama ke library akan berfungsi. Library menggunakan resource non-bersama yang diteruskan.
Jangan meneruskan resource yang tidak dibagikan sebagai parameter fungsi. Selalu deklarasikan resource yang tidak dibagikan dalam skrip yang sama dengan yang menggunakannya.
Pertimbangkan Proyek A dan Proyek B di mana Proyek A menggunakan Proyek B sebagai pustaka. Dalam contoh ini, PropertiesService
adalah resource non-bersama.
// 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 pada runtime V8, Anda harus menyediakan setidaknya melihat akses ke skrip agar pemicu skrip berfungsi dengan baik.