O ambiente de execução do Rhino será descontinuado em 31 de janeiro de 2026 ou após essa data. Se você tiver um script que usa o ambiente de execução do Rhino, migre-o para o V8.
Muitas vezes, o único pré-requisito para adicionar sintaxe e recursos do V8 a um script é ativar o ambiente de execução do V8. No entanto, há um pequeno conjunto de incompatibilidades e outras diferenças que podem fazer com que um script falhe ou se comporte de maneira inesperada no ambiente de execução do V8. Ao migrar um script para usar o V8, você precisa pesquisar esses problemas no projeto de script e corrigir os que encontrar.
Procedimento de migração do V8
Para migrar um script para o V8, siga este procedimento:
- Ative o tempo de execução do V8
para o script. O
runtimeVersion
pode ser verificado usando o manifesto do projeto do Apps Script. - Analise com atenção as seguintes incompatibilidades. Examine o script para determinar se alguma das incompatibilidades está presente. Se uma ou mais incompatibilidades estiverem presentes, ajuste o código do script para remover ou evitar o problema.
- Analise com atenção as outras diferenças a seguir. Examine o script para determinar se alguma das diferenças listadas afeta o comportamento do código. Ajuste o script para corrigir o comportamento.
- Depois de corrigir as incompatibilidades ou outras diferenças encontradas, comece a atualizar o código para usar a sintaxe do V8 e outros recursos.
- Depois de concluir os ajustes no código, teste o script para garantir que ele funcione como esperado.
- Se o script for um app da Web ou um complemento publicado, crie uma nova versão do script com os ajustes do V8 e direcione a implantação para a versão criada recentemente. Para disponibilizar a versão do V8 aos usuários, é necessário publicar o script novamente com essa versão.
- Se o script for usado como uma biblioteca, crie uma nova implantação versionada dele. Comunique essa nova versão a todos os scripts e usuários que consomem sua biblioteca, instruindo-os a atualizar para a versão compatível com V8. Verifique se as versões mais antigas da sua biblioteca, baseadas no Rhino, não estão mais em uso ativo ou acessíveis.
- Verifique se nenhuma instância do seu script ainda está operando no ambiente de execução do Rhino legado. Verifique se todas as implantações estão associadas a uma versão no V8. Arquive implantações antigas. Analise todas as versões e exclua as que não usam o ambiente de execução V8.
Incompatibilidades
Infelizmente, o ambiente de execução original do Apps Script baseado em Rhino permitia vários comportamentos não padrão do ECMAScript. Como o V8 está em conformidade com os padrões, esses comportamentos não são aceitos após a migração. Se esses problemas não forem corrigidos, haverá erros ou comportamento inadequado do script quando o tempo de execução do V8 for ativado.
As seções a seguir descrevem cada um desses comportamentos e as etapas que você precisa seguir para corrigir o código do script durante a migração para o V8.
Evite for each(variable in object)
A instrução
for each (variable in object)
foi adicionada ao JavaScript 1.6 e removida em favor de for...of
.
Ao migrar seu script para o V8, evite usar instruções for each (variable in object)
.
Em vez disso, use 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); } |
Evite Date.prototype.getYear()
No tempo de execução original do Rhino, Date.prototype.getYear()
retorna anos de dois dígitos para anos de 1900 a 1999, mas anos de quatro dígitos para outras datas, que era o comportamento no JavaScript 1.2 e versões anteriores.
No ambiente de execução V8,
Date.prototype.getYear()
retorna o ano menos 1900, conforme exigido pelos
padrões ECMAScript.
Ao migrar seu script para o V8, sempre use
Date.prototype.getFullYear()
,
que retorna um ano de quatro dígitos, independente da data.
Evite usar palavras-chave reservadas como nomes
O ECMAScript proíbe o uso de determinadas palavras-chave reservadas em nomes de funções e variáveis. O ambiente de execução do Rhino permitia muitas dessas palavras. Portanto, se o código as usar, renomeie as funções ou variáveis.
Ao migrar seu script para o V8, evite nomear variáveis ou funções
usando uma das
palavras-chave reservadas.
Renomeie qualquer variável ou função para evitar usar o nome da palavra-chave. Os usos comuns
de palavras-chave como nomes são class
, import
e export
.
Evite reatribuir variáveis const
No runtime original do Rhino, é possível declarar uma variável usando const
, o que significa que o valor do símbolo nunca muda e as atribuições futuras a ele são ignoradas.
No novo ambiente de execução do V8, a palavra-chave const
está em conformidade com o padrão, e a atribuição
a uma variável declarada como const
resulta em um
erro de tempo de execução TypeError: Assignment to constant variable
.
Ao migrar seu script para o V8, não tente reatribuir o valor de
uma variável 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 |
Evite literais XML e o objeto XML
Essa extensão não padrão do ECMAScript permite que projetos do Apps Script usem a sintaxe XML diretamente.
Ao migrar seu script para o V8, evite usar literais XML diretos ou o objeto XML.
Em vez disso, use o XmlService para analisar 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 |
Não crie funções de iterador personalizadas usando __iterator__
O JavaScript 1.7 adicionou um recurso para permitir a inclusão de um iterador personalizado em qualquer classe
declarando uma função __iterator__
no protótipo dessa classe. Isso também foi
adicionado ao tempo de execução do Rhino do Apps Script como uma
conveniência para desenvolvedores. No entanto, esse recurso nunca fez parte do padrão ECMA-262 e foi removido em mecanismos JavaScript compatíveis com ECMAScript. Scripts que usam V8 não podem usar essa construção de iterador.
Ao migrar seu script para o V8, evite a função __iterator__
para criar
iteradores personalizados. Em vez disso,
use iteradores ECMAScript 6.
Considere a seguinte construção de matriz:
// 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. |
Os exemplos de código a seguir mostram como um iterador pode ser construído no ambiente de execução do Rhino e como construir um iterador substituto no ambiente de execução do 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); } |
Evitar cláusulas de captura condicional
O ambiente de execução do V8 não oferece suporte a cláusulas de captura condicional catch..if
, porque elas não estão em conformidade com o padrão.
Ao migrar seu script para o V8, mova todas as condições de captura para dentro do corpo de captura:
// 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 } } |
Evite usar Object.prototype.toSource()
O JavaScript 1.3 continha um método Object.prototype.toSource() que nunca fez parte de nenhum padrão ECMAScript. Ele não é compatível com o ambiente de execução do V8.
Ao migrar seu script para o V8, remova qualquer uso de Object.prototype.toSource() do seu código.
Outras diferenças
Além das incompatibilidades anteriores que podem causar falhas de script, há algumas outras diferenças que, se não forem corrigidas, podem resultar em um comportamento inesperado do script de tempo de execução do V8.
As seções a seguir explicam como atualizar o código do script para evitar essas surpresas inesperadas.
Ajustar a formatação de data e hora específica da localidade
Os métodos Date
toLocaleString()
,
toLocaleDateString()
e toLocaleTimeString()
se comportam de maneira diferente no ambiente de execução do V8 em comparação com o Rhino.
No Rhino, o formato padrão é o longo, e todos os parâmetros transmitidos são ignorados.
No ambiente de execução V8, o formato padrão é o formato curto, e os parâmetros transmitidos são processados de acordo com o padrão ECMA. Consulte a documentação toLocaleDateString()
para mais detalhes.
Ao migrar seu script para o V8, teste e ajuste as expectativas do código em relação à saída de métodos de data e hora específicos da localidade:
// 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' })); |
Evite usar Error.fileName
e Error.lineNumber
No ambiente de execução do V8, o objeto JavaScript
Error
padrão não é compatível com fileName
ou lineNumber
como parâmetros de construtor
ou propriedades de objeto.
Ao migrar seu script para o V8,
remova qualquer dependência de Error.fileName
e Error.lineNumber
.
Uma alternativa é usar o
Error.prototype.stack
.
Essa pilha também não é padrão, mas tem suporte no V8. O formato do rastreamento de pilha produzido pelas duas plataformas é um pouco 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) |
Ajustar o processamento de objetos de enumeração stringificados
No ambiente de execução original do Rhino, usar o método JavaScript
JSON.stringify()
em um objeto de enumeração retorna apenas {}
.
No V8, usar o mesmo método em um objeto de enumeração retorna o nome da enumeração.
Ao migrar seu script para o V8,
teste e ajuste as expectativas do código em relação à saída de
JSON.stringify()
em objetos de enumeração:
// 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" |
Ajustar o processamento de parâmetros indefinidos
No ambiente de execução original do Rhino, transmitir undefined
para um método como parâmetro
resultava na transmissão da string "undefined"
para esse método.
No V8, transmitir undefined
para métodos é equivalente a transmitir null
.
Ao migrar seu script para o V8,
teste e ajuste as expectativas do código em relação aos 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. |
Ajustar o processamento de this
global
O ambiente de execução do Rhino define um contexto especial implícito para scripts que o usam.
O código do script é executado nesse contexto implícito, que é diferente do this
global real. Isso significa que as referências ao "this
global" no código são avaliadas como o contexto especial, que contém apenas o código e as variáveis definidos no script. Os serviços integrados do Apps Script e os objetos ECMAScript são excluídos desse uso de this
. Essa situação era semelhante a esta estrutura 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. }(); |
No V8, o contexto especial implícito é removido. Variáveis e funções globais definidas no script são colocadas no contexto global, ao lado dos serviços integrados do Apps Script e dos recursos integrados do ECMAScript, como Math
e Date
.
Ao migrar seu script para o V8, teste e ajuste as expectativas do código
em relação ao uso de this
em um contexto global. Na maioria dos casos, as diferenças só são aparentes se o código examinar as chaves ou os nomes de propriedades do objeto global 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)); } |
Ajustar o processamento de instanceof
em bibliotecas
Usar instanceof
em uma biblioteca em um objeto transmitido como parâmetro em uma
função de outro projeto pode gerar falsos negativos. No ambiente de execução V8, um projeto e as bibliotecas dele são executados em contextos de execução diferentes e, portanto, têm globais e cadeias de protótipos diferentes.
Isso só acontece se a biblioteca usar instanceof
em um objeto
que não foi criado no projeto. Usá-lo em um objeto criado no
seu projeto, seja no mesmo ou em um script diferente dentro do projeto,
deve funcionar como esperado.
Se um projeto em execução no V8 usar seu script como uma biblioteca, verifique se ele usa instanceof
em um parâmetro que será transmitido de outro projeto. Ajuste
o uso de instanceof
e use outras alternativas viáveis de acordo com seu caso
de uso.
Uma alternativa para a instanceof b
é usar o construtor de a
em
casos em que não é necessário pesquisar toda a cadeia de protótipos e apenas verificar
o construtor.
Uso: a.constructor.name == "b"
Considere o projeto A e o projeto B, em que o projeto A usa o projeto B como uma 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 } |
Outra alternativa é introduzir uma função que verifique instanceof
no projeto principal
e transmita a função além de outros parâmetros ao chamar uma função de biblioteca. A função transmitida
pode ser usada para verificar instanceof
na 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); } |
Ajustar a transmissão de recursos não compartilhados para bibliotecas
A transmissão de um recurso não compartilhado do script principal para uma biblioteca funciona de maneira diferente no ambiente de execução do V8.
Na execução do Rhino, não é possível transmitir um recurso não compartilhado. Em vez disso, a biblioteca usa o próprio recurso.
No tempo de execução do V8, transmitir um recurso não compartilhado para a biblioteca funciona. A biblioteca usa o recurso não compartilhado transmitido.
Não transmita recursos não compartilhados como parâmetros de função. Sempre declare recursos não compartilhados no mesmo script que os usa.
Considere o projeto A e o projeto B, em que o projeto A usa o projeto B como uma biblioteca. Neste exemplo, PropertiesService
é um recurso não compartilhado.
// 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')); } |
Atualizar o acesso a scripts independentes
Para scripts independentes executados no ambiente de execução V8, é necessário conceder aos usuários pelo menos acesso de leitura ao script para que os gatilhos dele funcionem corretamente.