Il runtime Rhino verrà disattivato a partire dal 31 gennaio 2026. Se hai uno script esistente che utilizza il runtime Rhino, devi eseguirne la migrazione a V8.
Spesso l'unico prerequisito per aggiungere la sintassi e le funzionalità V8 a uno script è l'attivazione del runtime V8. Tuttavia, esiste un piccolo insieme di incompatibilità e altre differenze che possono causare l'errore di uno script o un comportamento imprevisto nel runtime V8. Quando esegui la migrazione di uno script per utilizzare V8, devi cercare questi problemi nel progetto di script e correggere quelli che trovi.
Procedura di migrazione a V8
Per eseguire la migrazione di uno script a V8, segui questa procedura:
- Attiva il runtime V8
per lo script. Il
runtimeVersion
può essere controllato utilizzando il manifest per il progetto Apps Script. - Esamina attentamente le seguenti incompatibilità. Esamina lo script per determinare se sono presenti incompatibilità. In caso affermativo, modifica il codice dello script per rimuovere o evitare il problema.
- Esamina attentamente le seguenti altre differenze. Esamina lo script per determinare se una delle differenze elencate influisce sul comportamento del codice. Modifica lo script per correggere il comportamento.
- Dopo aver corretto eventuali incompatibilità o altre differenze rilevate, puoi iniziare ad aggiornare il codice per utilizzare la sintassi V8 e altre funzionalità.
- Dopo aver terminato le modifiche al codice, testa accuratamente lo script per assicurarti che si comporti come previsto.
- Se il tuo script è un'app web o un componente aggiuntivo pubblicato, devi creare una nuova versione dello script con le modifiche V8 e indirizzare il deployment alla versione appena creata. Per rendere la versione V8 disponibile agli utenti, devi ripubblicare lo script con questa versione.
- Se lo script viene utilizzato come libreria, crea una nuova implementazione con controllo delle versioni dello script. Comunica questa nuova versione a tutti gli script e gli utenti che utilizzano la tua libreria, invitandoli a eseguire l'aggiornamento alla versione abilitata per V8. Verifica che le versioni precedenti della libreria basate su Rhino non siano più in uso attivo o accessibili.
- Verifica che nessuna istanza dello script sia ancora in esecuzione nel runtime Rhino legacy. Verifica che tutti i deployment siano associati a una versione su V8. Archivia i deployment meno recenti. Esamina tutte le versioni ed elimina quelle che non utilizzano V8 Runtime.
Incompatibilità
Il runtime Apps Script originale basato su Rhino purtroppo consentiva diversi comportamenti ECMAScript non standard. Poiché V8 è conforme agli standard, questi comportamenti non sono supportati dopo la migrazione. Se non correggi questi problemi, si verificano errori o il comportamento dello script non funziona correttamente una volta attivato il runtime V8.
Le sezioni seguenti descrivono ciascuno di questi comportamenti e i passaggi da eseguire per correggere il codice dello script durante la migrazione a V8.
Evita for each(variable in object)
L'istruzione
for each (variable in object)
è stata aggiunta a JavaScript 1.6 e rimossa a favore di for...of
.
Quando esegui la migrazione dello script a V8, evita di utilizzare le istruzioni for each (variable in object)
.
Utilizza invece 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); } |
Evita Date.prototype.getYear()
Nel runtime Rhino originale,
Date.prototype.getYear()
restituisce anni a due cifre per gli anni dal 1900 al 1999, ma anni a quattro cifre per altre
date, che era il comportamento in JavaScript 1.2 e versioni precedenti.
Nell'ambiente di runtime V8,
Date.prototype.getYear()
restituisce l'anno meno 1900, come richiesto dagli
standard ECMAScript.
Quando esegui la migrazione dello script a V8, utilizza sempre
Date.prototype.getFullYear()
,
che restituisce un anno a quattro cifre indipendentemente dalla data.
Evita di utilizzare parole chiave riservate come nomi
ECMAScript vieta l'utilizzo di determinate parole chiave riservate nei nomi di funzioni e variabili. Il runtime Rhino consentiva l'utilizzo di molte di queste parole, quindi se il tuo codice le utilizza, devi rinominare le funzioni o le variabili.
Quando esegui la migrazione dello script a V8, evita di assegnare nomi a variabili o funzioni
utilizzando una delle
parole chiave riservate.
Rinomina qualsiasi variabile o funzione per evitare di utilizzare il nome della parola chiave. Gli utilizzi comuni delle parole chiave come nomi sono class
, import
e export
.
Evitare di riassegnare le variabili const
Nel runtime Rhino originale, puoi dichiarare una variabile utilizzando const
, il che significa che il valore del simbolo non cambia mai e le assegnazioni future al simbolo vengono ignorate.
Nel nuovo runtime V8, la parola chiave const
è conforme agli standard e l'assegnazione
a una variabile dichiarata come const
genera un
errore di runtime TypeError: Assignment to constant variable
.
Quando esegui la migrazione dello script a V8, non tentare di riassegnare il valore di
una variabile 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 |
Evita i valori letterali XML e l'oggetto XML
Questa estensione non standard di ECMAScript consente ai progetti Apps Script di utilizzare direttamente la sintassi XML.
Quando esegui la migrazione dello script a V8, evita di utilizzare valori letterali XML diretti o l'oggetto XML.
Utilizza invece XmlService per analizzare 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 |
Non creare funzioni di iteratore personalizzate utilizzando __iterator__
JavaScript 1.7 ha aggiunto una funzionalità per consentire l'aggiunta di un iteratore personalizzato a qualsiasi classe dichiarando una funzione __iterator__
nel prototipo della classe. Questa funzionalità è stata aggiunta anche al runtime Rhino di Apps Script per comodità degli sviluppatori. Tuttavia,
questa funzionalità non ha mai fatto parte dello
standard ECMA-262
ed è stata rimossa nei motori JavaScript conformi a ECMAScript. Gli script che utilizzano V8
non possono utilizzare questa costruzione di iteratori.
Quando esegui la migrazione dello script a V8, evita la funzione __iterator__
per creare
iteratori personalizzati. Utilizza invece gli iteratori ECMAScript 6.
Considera la seguente costruzione di array:
// 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. |
Gli esempi di codice riportati di seguito mostrano come creare un iteratore nell'ambiente di runtime Rhino e come creare un iteratore di sostituzione nell'ambiente 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); } |
Evitare clausole catch condizionali
Il runtime V8 non supporta le clausole catch condizionali catch..if
, in quanto
non sono conformi agli standard.
Quando esegui la migrazione dello script a V8, sposta tutte le condizioni catch all'interno del corpo 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 } } |
Evita di utilizzare Object.prototype.toSource()
JavaScript 1.3 conteneva un metodo Object.prototype.toSource() che non ha mai fatto parte di alcuno standard ECMAScript. Non è supportato nel runtime V8.
Quando esegui la migrazione dello script a V8, rimuovi qualsiasi utilizzo di Object.prototype.toSource() dal codice.
Altre differenze
Oltre alle incompatibilità precedenti che possono causare errori di script, esistono alcune altre differenze che, se non corrette, potrebbero comportare un comportamento imprevisto dello script di runtime V8.
Le sezioni seguenti spiegano come aggiornare il codice dello script per evitare queste sorprese inattese.
Modificare la formattazione di data e ora specifica per le impostazioni internazionali
I metodi Date
,
toLocaleString()
,
toLocaleDateString()
e toLocaleTimeString()
si comportano in modo diverso nel runtime V8 rispetto a Rhino.
In Rhino, il formato predefinito è il formato lungo e tutti i parametri passati vengono ignorati.
Nel runtime V8, il formato predefinito è il formato breve e i parametri
trasmessi vengono gestiti in base allo standard ECMA (per i dettagli, consulta la
documentazionetoLocaleDateString()
).
Quando esegui la migrazione dello script a V8, testa e modifica le aspettative del codice in merito all'output dei metodi di data e ora specifici per le impostazioni internazionali:
// 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' })); |
Evita di utilizzare Error.fileName
e Error.lineNumber
Nel runtime V8, l'oggetto JavaScript standard
Error
non supporta fileName
o lineNumber
come parametri del costruttore
o proprietà dell'oggetto.
Quando esegui la migrazione dello script a V8,
rimuovi qualsiasi dipendenza da Error.fileName
e Error.lineNumber
.
Un'alternativa è utilizzare
Error.prototype.stack
.
Questo stack non è standard, ma è supportato in V8. Il
formato dello stack trace prodotto dalle due piattaforme è leggermente diverso:
// 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) |
Modifica della gestione degli oggetti enum con rappresentazione di stringa
Nel runtime Rhino originale, l'utilizzo del metodo JavaScript
JSON.stringify()
su un oggetto enum restituisce solo {}
.
In V8, l'utilizzo dello stesso metodo su un oggetto enum restituisce il nome dell'enum.
Quando esegui la migrazione dello script a V8, testa e modifica le aspettative del codice in merito all'output di
JSON.stringify()
sugli oggetti 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" |
Modificare la gestione dei parametri non definiti
Nel runtime Rhino originale, il passaggio di undefined
a un metodo come parametro
comportava il passaggio della stringa "undefined"
a quel metodo.
In V8, passare undefined
ai metodi equivale a passare null
.
Quando esegui la migrazione dello script a V8,
testa e modifica le aspettative del codice in merito ai undefined
parametri:
// 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. |
Modifica la gestione di this
globale
Il runtime Rhino definisce un contesto speciale implicito per gli script che lo utilizzano.
Il codice dello script viene eseguito in questo contesto implicito, distinto dal contesto globale
this
. Ciò significa che i riferimenti al "this
globale" nel codice
vengono valutati nel contesto speciale, che contiene solo il codice e le variabili
definiti nello script. I servizi Apps Script integrati e gli oggetti ECMAScript sono esclusi da questo utilizzo di this
. Questa situazione era simile a questa
struttura 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. }(); |
In V8, il contesto speciale implicito viene rimosso. Le variabili e le funzioni globali
definite nello script vengono inserite nel contesto globale, accanto ai servizi
Apps Script integrati e alle funzionalità integrate di ECMAScript come Math
e
Date
.
Quando esegui la migrazione dello script a V8, testa e modifica le aspettative del codice
in merito all'utilizzo di this
in un contesto globale. Nella maggior parte dei casi, le differenze
sono evidenti solo se il codice esamina le chiavi o i nomi delle proprietà dell'oggetto
globale 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)); } |
Modificare la gestione di instanceof
nelle librerie
L'utilizzo di instanceof
in una libreria su un oggetto passato come parametro in una funzione di un altro progetto può generare falsi negativi. Nel runtime V8, un
progetto e le relative librerie vengono eseguiti in contesti di esecuzione diversi e pertanto hanno
globali e catene di prototipi diversi.
Tieni presente che ciò si verifica solo se la tua libreria utilizza instanceof
su un oggetto
che non è stato creato nel tuo progetto. L'utilizzo su un oggetto creato nel progetto, nello stesso script o in uno diverso all'interno del progetto, dovrebbe funzionare come previsto.
Se un progetto in esecuzione su V8 utilizza il tuo script come libreria, verifica se lo script utilizza instanceof
su un parametro che verrà passato da un altro progetto. Modifica
l'utilizzo di instanceof
e utilizza altre alternative fattibili in base al tuo caso
d'uso.
Un'alternativa per a instanceof b
può essere l'utilizzo del costruttore di a
nei casi in cui non è necessario cercare l'intera catena di prototipi e basta controllare il costruttore.
Utilizzo: a.constructor.name == "b"
Considera il progetto A e il progetto B, in cui il progetto A utilizza il progetto B come libreria.
//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 } |
Un'altra alternativa può essere quella di introdurre una funzione che controlla instanceof
nel progetto principale
e di passare la funzione insieme ad altri parametri quando si chiama una funzione di libreria. La funzione passata
può quindi essere utilizzata per controllare instanceof
all'interno della libreria.
//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); } |
Modificare il passaggio di risorse non condivise alle librerie
Il passaggio di una risorsa non condivisa dallo script principale a una libreria funziona in modo diverso nel runtime V8.
Nel runtime Rhino, il passaggio di una risorsa non condivisa non funziona. La libreria utilizza invece una propria risorsa.
Nel runtime V8, il passaggio di una risorsa non condivisa alla libreria funziona. La libreria utilizza la risorsa non condivisa passata.
Non trasmettere risorse non condivise come parametri di funzione. Dichiara sempre le risorse non condivise nello stesso script che le utilizza.
Considera il progetto A e il progetto B, in cui il progetto A utilizza il progetto B come libreria. In questo esempio, PropertiesService
è una risorsa non condivisa.
// 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')); } |
Aggiornare l'accesso agli script autonomi
Per gli script autonomi in esecuzione su runtime V8, devi fornire agli utenti almeno l'accesso in visualizzazione allo script affinché i trigger dello script funzionino correttamente.