Si tienes una secuencia de comandos existente que usa el entorno de ejecución de Rhino y quieres usarla de la sintaxis y las funciones de V8, debes migrar la secuencia de comandos a V8.
La mayoría de las secuencias de comandos escritas con el entorno de ejecución de Rhino pueden funcionar con un entorno de ejecución de V8 sin ajustes. A menudo, el único requisito previo para agregar sintaxis V8 y atributos de un script es habilitar el entorno de ejecución V8.
Sin embargo, hay un pequeño conjunto incompatibilidades y otras diferencias que pueden dar como resultado una secuencia de comandos o se comporte de manera inesperada después de habilitar el tiempo de ejecución V8. Durante la migración una secuencia de comandos para usar V8, debes buscar estos problemas en el proyecto y corregir cualquiera que encuentres.
Procedimiento de migración de V8
Para migrar una secuencia de comandos a V8, sigue este procedimiento:
- Habilitar el entorno de ejecución de V8 para el guion.
- Revise cuidadosamente las incompatibilidades. que se enumeran a continuación. Examina tu secuencia de comandos para determinar si alguna de las hay incompatibilidades. si hay una o más incompatibilidades ajusta el código de la secuencia de comandos para quitar o evitar el problema.
- Revisa cuidadosamente otras diferencias que se indican a continuación. Examina tu guion para determinar si alguna de las diferencias mencionadas afecta el comportamiento de tu código. Ajusta el guion para corregir el comportamiento.
- Una vez que hayas corregido las incompatibilidades descubiertas o cualquier otra diferencia, puedes comenzar a actualizar tu código para usar la sintaxis de V8 y otras funciones según lo desees.
- Cuando termines los ajustes de código, prueba minuciosamente la secuencia de comandos para para asegurarte de que se comporte como se espera.
- Si tu secuencia de comandos es una app web o un complemento publicado, debes crear una versión nueva de la secuencia de comandos con los ajustes de V8. Para que la versión V8 esté disponible para los usuarios, debes volver a publicar la secuencia de comandos con esta versión.
Incompatibilidades
Lamentablemente, el tiempo de ejecución original de Apps Script basado en Rhino permitía varias comportamientos no estándar de ECMAScript. Como V8 cumple con los estándares, estos no se admiten después de la migración. No corregir estos problemas genera errores o un comportamiento incorrecto de la secuencia de comandos una vez que se habilita el tiempo de ejecución V8.
En las siguientes secciones, se describe cada uno de estos comportamientos y pasos que debes seguir para corregir tu código de secuencia de comandos durante la migración a V8.
Evita for each(variable in object)
El
for each (variable in object)
se agregó a JavaScript 1.6 y se quitó a favor de for...of
.
Cuando migres tu secuencia de comandos a V8, evita usar for each (variable in object)
.
de instrucciones.
En su lugar, usa 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()
En el entorno de ejecución original de Rhino,
Date.prototype.getYear()
devuelve los años de dos dígitos para los años 1900-1999, pero los años de cuatro dígitos para los otros
fechas, que era el comportamiento en JavaScript 1.2 y anteriores.
En el tiempo de ejecución V8,
Date.prototype.getYear()
devuelve el año menos 1900, según lo requiera
estándares ECMAScript.
Cuando migres tu secuencia de comandos a V8, usa siempre
Date.prototype.getFullYear()
,
que devuelve un año de cuatro dígitos independientemente de la fecha.
Evita usar palabras clave reservadas como nombres
ECMAScript prohíbe el uso de ciertas palabras clave reservadas en los nombres de funciones y variables. El entorno de ejecución de Rhino permitía muchas de estas palabras, así que, si tu código las usa, debes cambiar el nombre de tus funciones o variables.
Cuando migres la secuencia de comandos a V8, evita nombrar variables o funciones
mediante una de las
palabras clave reservadas.
Cambia el nombre de cualquier variable o función para evitar usar el nombre de la palabra clave. Usos comunes
de las palabras clave como nombres son class
, import
y export
.
Evita reasignar variables const
En el entorno de ejecución original de Rhino, puedes declarar una variable con const
, lo que significa que el valor del símbolo nunca cambia y se ignoran las asignaciones futuras al símbolo.
En el nuevo entorno de ejecución V8, la palabra clave const
cumple con los estándares y asigna
a una variable declarada como const
genera un
Error de entorno de ejecución de TypeError: Assignment to constant variable
.
Cuando migres tu secuencia de comandos a V8, no intentes reasignar el valor del
Una variable 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 los literales XML y el objeto XML
Esta extensión no estándar a ECMAScript permite que los proyectos de Apps Script usen la sintaxis XML directamente.
Cuando migres tu secuencia de comandos a V8, evita usar literales XML directos o el lenguaje un objeto.
En su lugar, usa XmlService para analizar 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 |
No compiles funciones de iterador personalizadas con __iterator__
.
En JavaScript 1.7 se agregó una función para permitir la adición de un iterador personalizado a cualquier clase.
declara una función __iterator__
en el prototipo de esa clase. esto era
también se agregó al entorno de ejecución de Rhino de Apps Script para mayor comodidad del desarrollador. Sin embargo,
esta función nunca fue parte
Estándar ECMA-262
y se quitó en los motores de JavaScript que cumplen con ECMAScript. Secuencias de comandos con V8
no se puede usar esta construcción de iterador.
Cuando migres tu secuencia de comandos a V8, evita la función __iterator__
para compilar
iteradores personalizados. Más bien,
usar iteradores de ECMAScript 6.
Considera la siguiente construcción del 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. |
En los siguientes ejemplos de código, se muestra cómo se puede construir un iterador en el Tiempo de ejecución de Rhino y cómo construir un iterador de reemplazo en el entorno de ejecución 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); } |
Evita las cláusulas de captura condicionales
El entorno de ejecución V8 no admite cláusulas de captura condicional catch..if
, ya que pueden
no cumplen con los estándares.
Cuando migres tu secuencia de comandos a V8, traslada los condicionales de captura dentro del Capturar cuerpo:
// 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 usar Object.prototype.toSource()
JavaScript 1.3 contenía un método Object.prototype.toSource() que nunca formó parte de ningún estándar de ECMAScript. No se admite en tiempo de ejecución V8.
Cuando migres la secuencia de comandos a V8, quita cualquier uso de Object.prototype.toSource() de tu código.
Otras diferencias
Además de las incompatibilidades anteriores que pueden causar fallas en la secuencia de comandos, existen algunas otras diferencias que, si no se corrigen, pueden generar un comportamiento inesperado de la secuencia de comandos del entorno de ejecución de V8.
En las siguientes secciones, se explica cómo actualizar el código de la secuencia de comandos para evitar estas sorpresas inesperadas.
Cómo ajustar el formato de fecha y hora específico de la configuración regional
El Date
métodos toLocaleString()
,
toLocaleDateString()
,
y toLocaleTimeString()
se comportan
en el tiempo de ejecución V8
en comparación con Rhino.
En Rhino, el formato predeterminado es el formato largo, y se ignoran los parámetros que se pasan.
En el entorno de ejecución de V8, el formato predeterminado es el formato corto, y los parámetros que se pasan se controlan según el estándar ECMA (consulta la documentación de toLocaleDateString()
para obtener más detalles).
Cuando migres tu secuencia de comandos a V8, prueba y ajusta las expectativas de tu código sobre el resultado de métodos de fecha y hora específicos de la configuración regional:
// 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 usar Error.fileName
y Error.lineNumber
En V8 untime, el código estándar JavaScript
Error
El objeto no admite fileName
ni lineNumber
como parámetros del constructor.
o propiedades del objeto.
Cuando migres tu secuencia de comandos a V8,
Quita cualquier dependencia de Error.fileName
y Error.lineNumber
.
Una alternativa es usar el
Error.prototype.stack
Esta pila también es no estándar, pero es compatible con Rhino y V8. El formato del seguimiento de pila que producen las dos plataformas es ligeramente diferente:
// 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) |
Cómo ajustar el manejo de objetos de enumeración enum en cadena
En el entorno de ejecución original de Rhino, con JavaScript
JSON.stringify()
de un objeto enum solo muestra {}
.
En V8, el uso del mismo método en un objeto enum recupera el nombre enum.
Cuando migres tu secuencia de comandos a V8,
probar y ajustar las expectativas de tu código con respecto a la salida
JSON.stringify()
En objetos de tipo 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" |
Cómo ajustar el manejo de parámetros no definidos
En el entorno de ejecución original de Rhino, pasa undefined
a un método como parámetro.
se pasó la cadena "undefined"
a ese método.
En V8, pasar undefined
a los métodos equivale a pasar null
.
Cuando migres tu secuencia de comandos a V8,
Prueba y ajusta las expectativas de tu código en relación con los parámetros 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. |
Ajusta el manejo de los this
globales
El entorno de ejecución de Rhino define un contexto especial implícito para las secuencias de comandos que lo usan.
El código de secuencia de comandos se ejecuta en este contexto implícito, distinto del código global real
this
Esto significa que las referencias al "this
global" en el código
evalúa en el contexto especial, que solo contiene el código y las variables
definidos en el script. Los servicios integrados de Apps Script y los objetos de ECMAScript
se excluyen de este uso de this
. Esta situación era similar a esta
estructura de 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. }(); |
En V8, se quita el contexto especial implícito. Funciones y variables globales
definidos en la secuencia de comandos se ubican en el contexto global, junto a la
Servicios de Apps Script y funciones integradas de ECMAScript, como Math
y Date
Cuando migres tu secuencia de comandos a V8, prueba y ajusta las expectativas de tu código
respecto al uso de this
en un contexto global. En la mayoría de los casos, las diferencias
solo son evidentes si tu código examina las claves o los nombres de las propiedades
objeto 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)); } |
Ajusta el manejo de instanceof
en las bibliotecas
Usar instanceof
en una biblioteca de un objeto que se pasa como parámetro en un
función de otro proyecto puede dar falsos negativos. En el tiempo de ejecución V8, un
proyecto y sus bibliotecas se ejecutan en diferentes contextos de ejecución y, por lo tanto, tienen
diferentes globales y cadenas de prototipos.
Ten en cuenta que esto solo ocurre si tu biblioteca usa instanceof
en un objeto que no se crea en tu proyecto. Usarlo en un objeto que se crea en tu proyecto, ya sea en la misma secuencia de comandos o en una diferente dentro de tu proyecto, debería funcionar como se espera.
Si un proyecto que se ejecuta en V8 usa tu secuencia de comandos como biblioteca, comprueba si
La secuencia de comandos usa instanceof
en un parámetro que se pasará desde otro proyecto. Ajustar
el uso de instanceof
y otras alternativas posibles según tu uso
caso.
Una alternativa para a instanceof b
puede ser usar el constructor de a
en
casos en los que no necesitas buscar toda la cadena de prototipos y solo comprobar
el constructor.
Uso: a.constructor.name == "b"
Considera el Proyecto A y el Proyecto B, en el que el Proyecto A usa el Proyecto B como biblioteca.
//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 } |
Otra alternativa puede ser ingresar una función que verifique instanceof
en el proyecto principal.
y pasar la función además de otros parámetros cuando llames a una función de biblioteca. La función pasada
se puede usar para buscar instanceof
dentro de la biblioteca.
//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); } |
Cómo ajustar el paso de recursos no compartidos a las bibliotecas
Pasar un recurso no compartido de la secuencia de comandos principal a una biblioteca funciona de manera diferente en el tiempo de ejecución V8.
En el entorno de ejecución de Rhino, pasar un recurso no compartido no funcionará. En su lugar, la biblioteca usa su propio recurso.
En el entorno de ejecución de V8, funciona pasar un recurso no compartido a la biblioteca. La biblioteca usa el recurso no compartido que se pasó.
No pases recursos no compartidos como parámetros de función. Siempre declara los recursos no compartidos en la misma secuencia de comandos que los usa.
Considera el proyecto A y el proyecto B, en el que el proyecto A usa el proyecto B como biblioteca. En este ejemplo, PropertiesService
es un recurso no compartido.
// 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')); } |
Actualiza el acceso a secuencias de comandos independientes
Para las secuencias de comandos independientes que se ejecutan en el tiempo de ejecución V8, debes proporcionar a los usuarios al menos el acceso de lectura a la secuencia de comandos para que los activadores funcionen correctamente.