L'environnement d'exécution Rhino sera désactivé le 31 janvier 2026 ou après cette date. Si vous disposez d'un script existant qui utilise l'environnement d'exécution Rhino, vous devez le migrer vers V8.
Souvent, la seule condition préalable à l'ajout de la syntaxe et des fonctionnalités V8 à un script consiste à activer l'environnement d'exécution V8. Toutefois, il existe un petit ensemble d'incompatibilités et d'autres différences qui peuvent entraîner l'échec d'un script ou un comportement inattendu dans l'environnement d'exécution V8. Lorsque vous migrez un script pour utiliser V8, vous devez rechercher ces problèmes dans le projet de script et les corriger.
Procédure de migration vers V8
Pour migrer un script vers V8, procédez comme suit :
- Activez l'environnement d'exécution V8
pour le script. Le
runtimeVersionpeut être vérifié à l'aide du fichier manifeste du projet Google Apps Script. - Examinez attentivement les incompatibilités suivantes. Vérifiez si votre script présente l'une de ces incompatibilités. Si c'est le cas, ajustez le code de votre script pour supprimer ou éviter le problème.
- Examinez attentivement les autres différences suivantes. Vérifiez si l'une des différences listées a un impact sur le comportement de votre code. Ajustez votre script pour corriger le comportement.
- Une fois que vous avez corrigé les incompatibilités ou autres différences détectées, commencez à mettre à jour votre code pour utiliser la syntaxe V8 et d'autres fonctionnalités.
- Une fois les ajustements de code terminés, testez minutieusement votre script pour vous assurer qu'il se comporte comme prévu.
- Si votre script est une application Web ou un module complémentaire publié , vous devez créer une nouvelle version du script avec les ajustements V8 et pointer le déploiement vers la version que vous venez de créer. Pour que la version V8 soit disponible pour les utilisateurs, vous devez republier le script avec cette version.
- Si votre script est utilisé comme bibliothèque, créez un déploiement avec version de votre script. Communiquez cette nouvelle version à tous les scripts et utilisateurs qui consomment votre bibliothèque, en leur demandant de passer à la version compatible avec V8. Vérifiez que les anciennes versions de votre bibliothèque basées sur Rhino ne sont plus utilisées ni accessibles.
- Vérifiez qu'aucune instance de votre script ne fonctionne encore sur l'ancien environnement d'exécution Rhino. Vérifiez que tous les déploiements sont associés à une version qui utilise V8. Archivez les anciens déploiements. Passez en revue toutes les versions et supprimez celles qui n'utilisent pas l'environnement d'exécution V8.
Incompatibilités
L'environnement d'exécution Apps Script d'origine basé sur Rhino autorisait malheureusement plusieurs comportements ECMAScript non standards. Comme V8 est conforme aux normes, ces comportements ne sont plus compatibles après la migration. Si vous ne corrigez pas ces problèmes, des erreurs se produiront ou le script ne fonctionnera plus une fois l'environnement d'exécution V8 activé.
Les sections suivantes décrivent chacun de ces comportements et les étapes à suivre pour corriger le code de votre script lors de la migration vers V8.
Éviter for each(variable in object)
L'
for each (variable in object)
instruction a été ajoutée à JavaScript 1.6 et supprimée au profit de for...of.
Lorsque vous migrez votre script vers V8, évitez d'utiliser des for each (variable in object)
instructions.
Utilisez plutôt 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); } |
Éviter Date.prototype.getYear()
Dans l'environnement d'exécution Rhino d'origine,
Date.prototype.getYear()
renvoie des années à deux chiffres pour les années 1900 à 1999, mais des années à quatre chiffres pour les autres
dates, ce qui était le comportement dans JavaScript 1.2 et versions antérieures.
Dans l'environnement d'exécution V8,
Date.prototype.getYear()
renvoie l'année moins 1900, comme l'exigent les normes ECMAScript.
Lorsque vous migrez votre script vers V8, utilisez toujours
Date.prototype.getFullYear(),
qui renvoie une année à quatre chiffres, quelle que soit la date.
Éviter d'utiliser des mots clés réservés comme noms
ECMAScript interdit l'utilisation de certains mots clés réservés dans les noms de fonctions et de variables. L'environnement d'exécution Rhino autorisait l'utilisation de nombreux mots de ce type. Par conséquent, si votre code les utilise, vous devez renommer vos fonctions ou variables.
Lorsque vous migrez votre script vers V8, évitez de nommer des variables ou des fonctions à l'aide de l'un des
mots clés
réservés.
Renommez toute variable ou fonction pour éviter d'utiliser le nom du mot clé. class, import et export sont des utilisations courantes de mots clés comme noms.
Une exception est que les littéraux d'objet sont autorisés à utiliser des mots clés réservés (dans tous les environnements d'exécution) :
function class() {} // Syntax error in V8. var obj = { class: 1 }; // Allowed.
Éviter de réattribuer des variables const
Dans l'environnement d'exécution Rhino d'origine, vous pouvez déclarer une variable à l'aide de const, ce qui signifie que la valeur du symbole ne change jamais et que les futures affectations au symbole sont ignorées.
Dans le nouvel environnement d'exécution V8, le const mot clé est conforme aux normes et l'attribution
à une variable déclarée comme un const génère une
TypeError: Assignment to constant variable erreur d'exécution.
Lorsque vous migrez votre script vers V8, n'essayez pas de réattribuer la valeur d'une
const variable :
// 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 |
Éviter les littéraux XML et l'objet XML
Cette extension non standard d'ECMAScript permet aux projets Apps Script d'utiliser directement la syntaxe XML.
Lorsque vous migrez votre script vers V8, évitez d'utiliser des littéraux XML directs ou l'objet XML.
Utilisez plutôt XmlService pour analyser le code 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 |
Ne créez pas de fonctions d'itérateur personnalisées à l'aide de __iterator__
JavaScript 1.7 a ajouté une fonctionnalité permettant d'ajouter un itérateur personnalisé à n'importe quelle classe en déclarant une fonction __iterator__ dans le prototype de cette classe. Cette fonctionnalité a également été ajoutée à l'environnement d'exécution Rhino d'Apps Script pour faciliter la tâche des développeurs. Toutefois, cette fonctionnalité n'a jamais fait partie de la
norme ECMA-262
et a été supprimée dans les moteurs JavaScript conformes à ECMAScript. Les scripts utilisant V8 ne peuvent pas utiliser cette construction d'itérateur.
Lorsque vous migrez votre script vers V8, évitez d'utiliser la fonction __iterator__ pour créer des
itérateurs personnalisés. Utilisez plutôt les
itérateurs ECMAScript 6.
Prenons l'exemple de la construction de tableau suivante :
// 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. |
Les exemples de code suivants montrent comment un itérateur peut être construit dans l'environnement d'exécution Rhino et comment construire un itérateur de remplacement dans l'environnement d'exécution 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); } |
Dans l'environnement d'exécution V8, vous devez utiliser for...of lorsque vous parcourez des tableaux avec des itérateurs personnalisés, car for..in ne s'attend pas à des itérables.
Éviter les clauses catch conditionnelles
L'environnement d'exécution V8 n'est pas compatible avec les clauses catch conditionnelles catch..if, car elles ne sont pas conformes aux normes.
Lorsque vous migrez votre script vers V8, déplacez toutes les conditions catch à l'intérieur du corps 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 } } |
Éviter d'utiliser Object.prototype.toSource()
JavaScript 1.3 contenait une Object.prototype.toSource() qui n'a jamais fait partie d'une norme ECMAScript. Elle n'est pas compatible avec l'environnement d'exécution V8.
Lorsque vous migrez votre script vers V8, supprimez toute utilisation de Object.prototype.toSource() de votre code.
Autres différences
Outre les incompatibilités précédentes qui peuvent entraîner des échecs de script, il existe quelques autres différences qui, si elles ne sont pas corrigées, peuvent entraîner un comportement inattendu du script dans l'environnement d'exécution V8.
Les sections suivantes expliquent comment mettre à jour le code de votre script pour éviter ces surprises inattendues.
Ajuster la mise en forme des dates et heures spécifiques aux paramètres régionaux
Les Date
méthodes toLocaleString(),
toLocaleDateString(),
et toLocaleTimeString()
se comportent différemment dans l'environnement d'exécution V8 par rapport à Rhino.
Dans Rhino, le format par défaut est le format long, et tous les paramètres transmis sont ignorés.
Dans l'environnement d'exécution V8, le format par défaut est le format court et les paramètres
transmis sont gérés conformément à la norme ECMA (pour en savoir plus, consultez la
toLocaleDateString() documentation
).
Lorsque vous migrez votre script vers V8, testez et ajustez les attentes de votre code concernant la sortie des méthodes de date et d'heure spécifiques aux paramètres régionaux :
// 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' })); |
Éviter d'utiliser Error.fileName et Error.lineNumber
Dans l'environnement d'exécution V8, l'objet JavaScript standard
Error
n'est pas compatible avec fileName ni lineNumber en tant que paramètres de constructeur
ou propriétés d'objet.
Lorsque vous migrez votre script vers V8,
supprimez toute dépendance à Error.fileName et Error.lineNumber.
Vous pouvez également utiliser le
Error.prototype.stack.
Cette pile n'est pas non plus standard, mais elle est compatible avec V8. Le format de la trace de pile produite par les deux plates-formes est légèrement différent :
// 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) |
Ajuster la gestion des objets enum sérialisés
Dans l'environnement d'exécution Rhino d'origine, l'utilisation de la méthode JavaScript
JSON.stringify()
sur un objet enum ne renvoie que {}.
Dans V8, l'utilisation de la même méthode sur un objet enum renvoie le nom de l'enum.
Lorsque vous migrez votre script vers V8, testez et ajustez les attentes de votre code
concernant le résultat de
JSON.stringify()
sur les objets 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" |
Ajuster la gestion des paramètres non définis
Dans l'environnement d'exécution Rhino d'origine, la transmission de undefined à une méthode en tant que paramètre
entraînait la transmission de la chaîne "undefined" à cette méthode.
Dans V8, la transmission de undefined aux méthodes équivaut à la transmission de null.
Lorsque vous migrez votre script vers V8, testez et ajustez les attentes de votre code
concernant les paramètres 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. |
Ajuster la gestion de this global
L'environnement d'exécution Rhino définit un contexte spécial implicite pour les scripts qui l'utilisent.
Le code de script s'exécute dans ce contexte implicite, distinct du this global réel. Cela signifie que les références au " this global" dans le code sont en fait évaluées dans le contexte spécial, qui ne contient que le code et les variables définis dans le script. Les services Apps Script intégrés et les objets ECMAScript sont exclus de cette utilisation de this. Cette situation était semblable à la structure JavaScript suivante :
// 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. }(); |
Dans V8, le contexte spécial implicite est supprimé. Les variables et fonctions globales définies dans le script sont placées dans le contexte global, à côté des services Apps Script intégrés et des éléments intégrés ECMAScript tels que Math et Date.
Lorsque vous migrez votre script vers V8, testez et ajustez les attentes de votre code
concernant l'utilisation de this dans un contexte global. Dans la plupart des cas, les différences ne sont apparentes que si votre code examine les clés ou les noms de propriétés de l'objet 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)); } |
Ajuster la gestion de instanceof dans les bibliothèques
L'utilisation de instanceof dans une bibliothèque sur un objet transmis en tant que paramètre dans une fonction d'un autre projet peut générer de faux négatifs. Dans l'environnement d'exécution V8, un projet et ses bibliothèques s'exécutent dans des contextes d'exécution différents et ont donc des chaînes globales et prototypes différentes.
Cela ne se produit que si votre bibliothèque utilise instanceof sur un objet qui n'est pas créé dans votre projet. Son utilisation sur un objet créé dans votre projet, que ce soit dans le même script ou dans un script différent de votre projet, devrait fonctionner comme prévu.
Si un projet exécuté sur V8 utilise votre script comme bibliothèque, vérifiez si votre
script utilise instanceof sur un paramètre transmis à partir d'un autre projet.
Ajustez l'utilisation de instanceof et utilisez d'autres alternatives possibles en fonction de votre cas d'utilisation.
Une alternative à a instanceof b peut consister à utiliser le constructeur de a dans
les cas où vous n'avez pas besoin de rechercher l'ensemble de la chaîne de prototypes et où vous vérifiez simplement
le constructeur. Utilisation : a.constructor.name == "b"
Prenons l'exemple du projet A et du projet B, où le projet A utilise le projet B comme bibliothèque.
//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 } |
Une autre solution consiste à introduire une fonction qui vérifie instanceof dans le projet principal et à transmettre la fonction en plus d'autres paramètres lors de l'appel d'une fonction de bibliothèque. La fonction transmise peut ensuite être utilisée pour vérifier instanceof dans la bibliothèque.
//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); } |
Ajuster la transmission de ressources non partagées aux bibliothèques
La transmission d'une ressource non partagée du script principal à une bibliothèque fonctionne différemment dans l'environnement d'exécution V8.
Dans l'environnement d'exécution Rhino, la transmission d'une ressource non partagée ne fonctionne pas. La bibliothèque utilise sa propre ressource à la place.
Dans l'environnement d'exécution V8, la transmission d'une ressource non partagée à la bibliothèque fonctionne. La bibliothèque utilise la ressource non partagée transmise.
Ne transmettez pas de ressources non partagées en tant que paramètres de fonction. Déclarez toujours les ressources non partagées dans le même script que celui qui les utilise.
Prenons l'exemple du projet A et du projet B, où le projet A utilise le projet B comme bibliothèque. Dans cet exemple, PropertiesService est une ressource non partagée.
// 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')); } |
Recommandations JDBC dans l'environnement d'exécution V8
Avec l'environnement d'exécution V8, nous avons ajouté de nouvelles fonctionnalités au service JDBC.
Utiliser executeBatch pour les opérations par lots
Utilisez les opérations executeBatch(params) pour effectuer des opérations de base de données par lots.
L'exemple suivant montre comment insérer plusieurs lignes dans une base de données à l'aide du traitement par lots :
Voici l'environnement d'exécution Rhino (ancienne méthode) :
var conn = Jdbc.getCloudSqlConnection("jdbc:google:mysql://..."); var stmt = conn.prepareStatement("INSERT INTO employees (name, age) VALUES (?, ?)"); var params = [["John Doe", 30], ["John Smith", 25]]; for (var i = 0; i < params.length; i++) { stmt.setString(1, params[i][0]); stmt.setInt(2, params[i][1]); stmt.execute(); }
Voici l'environnement d'exécution V8 (nouvelle méthode) :
var conn = Jdbc.getCloudSqlConnection("jdbc:google:mysql://..."); var stmt = conn.prepareStatement("INSERT INTO employees (name, age) VALUES (?, ?)"); var params = [["John Doe", 30], ["John Smith", 25]]; stmt.executeBatch(params);
Utiliser getRows pour récupérer l'ensemble de résultats
Utilisez getRows(queryString) pour récupérer les données de l'ensemble de résultats en un seul appel. The
queryString se compose d'appels séparés par une virgule aux méthodes getter de
JdbcResultSet, par exemple : "getString(1), getDouble('price'), getDate(3,
'UTC')". Les méthodes compatibles incluent toutes les méthodes getter chargées de lire les données de colonne. Par exemple, getHoldability, getMetaData, etc. ne sont pas compatibles. Les arguments peuvent être des index de colonne entiers (à partir de 1) ou des libellés de colonne de chaîne entre guillemets simples ou doubles.
L'exemple suivant montre comment récupérer des lignes à partir de l'ensemble de résultats :
Voici l'environnement d'exécution Rhino (ancienne méthode) :
var conn = Jdbc.getCloudSqlConnection("jdbc:google:mysql://..."); var stmt = conn.createStatement(); var rs = stmt.executeQuery("SELECT name, age FROM employees"); while (rs.next()) { Logger.log(rs.getString('name') + ", " + rs.getInt('age')); }
Voici l'environnement d'exécution V8 (nouvelle méthode) :
var conn = Jdbc.getCloudSqlConnection("jdbc:google:mysql://..."); var stmt = conn.createStatement(); var rs = stmt.executeQuery("SELECT name, age FROM employees"); var rows = rs.getRows("getString('name'), getInt('age')"); for (var i = 0; i < rows.length; i++) { Logger.log(rows[i][0] + ", " + rows[i][1]); }
Mettre à jour l'accès aux scripts autonomes
Pour que les déclencheurs d'un script autonome exécuté dans l'environnement d'exécution V8 fonctionnent correctement, vous devez au moins accorder aux utilisateurs un accès en lecture au script.