Scripts zur V8-Laufzeit migrieren

Die Rhino-Laufzeitumgebung wird am oder nach dem 31. Januar 2026 eingestellt. Wenn Sie ein vorhandenes Script mit der Rhino-Laufzeit haben, müssen Sie es zu V8 migrieren.

Die einzige Voraussetzung für das Hinzufügen von V8-Syntax und ‑Funktionen zu einem Skript ist häufig das Aktivieren der V8-Laufzeit. Es gibt jedoch einige Inkompatibilitäten und andere Unterschiede, die dazu führen können, dass ein Skript in der V8-Laufzeit fehlschlägt oder sich unerwartet verhält. Wenn Sie ein Skript für die Verwendung von V8 migrieren, müssen Sie das Skriptprojekt nach diesen Problemen durchsuchen und alle gefundenen Probleme beheben.

V8-Migrationsverfahren

So migrieren Sie ein Skript zu V8:

  1. Aktivieren Sie die V8-Laufzeit für das Script. Die runtimeVersion kann im Manifest des Apps Script-Projekts geprüft werden.
  2. Sehen Sie sich die folgenden Inkompatibilitäten genau an. Prüfen Sie Ihr Skript, um festzustellen, ob eine der Inkompatibilitäten vorliegt. Wenn eine oder mehrere Inkompatibilitäten vorhanden sind, passen Sie den Skriptcode an, um das Problem zu beheben oder zu vermeiden.
  3. Sehen Sie sich die folgenden weiteren Unterschiede genau an. Prüfen Sie Ihr Skript, um festzustellen, ob sich die aufgeführten Unterschiede auf das Verhalten Ihres Codes auswirken. Passen Sie das Skript an, um das Verhalten zu korrigieren.
  4. Nachdem Sie alle gefundenen Inkompatibilitäten oder anderen Unterschiede behoben haben, können Sie Ihren Code aktualisieren, um V8-Syntax und andere Funktionen zu verwenden.
  5. Nachdem Sie die Codeanpassungen vorgenommen haben, sollten Sie Ihr Script gründlich testen, um sicherzugehen, dass es sich wie erwartet verhält.
  6. Wenn Ihr Skript eine Web-App oder ein veröffentlichtes Add-on ist, müssen Sie eine neue Version des Skripts mit den V8-Anpassungen erstellen und die Bereitstellung auf die neu erstellte Version verweisen. Damit die V8-Version für Nutzer verfügbar ist, müssen Sie das Skript mit dieser Version noch einmal veröffentlichen.
  7. Wenn Ihr Script als Bibliothek verwendet wird, erstellen Sie eine neue versionierte Bereitstellung des Scripts. Informieren Sie alle Skripts und Nutzer, die Ihre Bibliothek verwenden, über diese neue Version und weisen Sie sie an, auf die V8-fähige Version zu aktualisieren. Prüfen Sie, ob ältere, auf Rhino basierende Versionen Ihrer Bibliothek nicht mehr aktiv verwendet werden oder zugänglich sind.
  8. Prüfen Sie, ob noch Instanzen Ihres Skripts in der alten Rhino-Laufzeitumgebung ausgeführt werden. Prüfen Sie, ob alle Bereitstellungen einer Version zugeordnet sind, die auf V8 basiert. Alte Bereitstellungen archivieren. Sehen Sie sich alle Versionen an und löschen Sie die Versionen, die nicht die V8-Laufzeit verwenden.

Inkompatibilitäten

Die ursprüngliche Rhino-basierte Apps Script-Laufzeitumgebung hat leider mehrere nicht standardmäßige ECMAScript-Verhaltensweisen zugelassen. Da V8 standardkonform ist, werden diese Verhaltensweisen nach der Migration nicht mehr unterstützt. Wenn Sie diese Probleme nicht beheben, treten Fehler auf oder das Skript funktioniert nicht mehr, sobald die V8-Laufzeitumgebung aktiviert ist.

In den folgenden Abschnitten werden diese Verhaltensweisen und die Schritte beschrieben, die Sie ausführen müssen, um Ihren Skriptcode während der Migration zu V8 zu korrigieren.

Auf for each(variable in object) verzichten

Die Anweisung for each (variable in object) wurde in JavaScript 1.6 hinzugefügt und zugunsten von for...of entfernt.

Vermeiden Sie bei der Migration Ihres Skripts zu V8 die Verwendung von for each (variable in object)-Anweisungen.

Verwenden Sie stattdessen 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);
}
      

Auf Date.prototype.getYear() verzichten

In der ursprünglichen Rhino-Laufzeitumgebung gibt Date.prototype.getYear() für die Jahre 1900 bis 1999 zweistellige Jahreszahlen zurück, für andere Datumsangaben jedoch vierstellige Jahreszahlen. Das war das Verhalten in JavaScript 1.2 und früher.

In der V8-Laufzeit gibt Date.prototype.getYear() das Jahr minus 1900 zurück, wie es in den ECMAScript-Standards festgelegt ist.

Verwenden Sie beim Migrieren Ihres Skripts zu V8 immer Date.prototype.getFullYear(), da diese Funktion unabhängig vom Datum ein vierstelliges Jahr zurückgibt.

Reservierte Keywords nicht als Namen verwenden

ECMAScript verbietet die Verwendung bestimmter reservierter Schlüsselwörter in Funktions- und Variablennamen. Die Rhino-Laufzeit hat viele dieser Wörter zugelassen. Wenn Ihr Code sie verwendet, müssen Sie Ihre Funktionen oder Variablen umbenennen.

Vermeiden Sie beim Migrieren Ihres Skripts zu V8, Variablen oder Funktionen mit einem der reservierten Schlüsselwörter zu benennen. Benennen Sie alle Variablen oder Funktionen um, damit das Schlüsselwort nicht verwendet wird. Gängige Verwendungen von Keywords als Namen sind class, import und export.

const-Variablen nicht neu zuweisen

In der ursprünglichen Rhino-Laufzeit können Sie eine Variable mit const deklarieren. Das bedeutet, dass sich der Wert des Symbols nie ändert und zukünftige Zuweisungen an das Symbol ignoriert werden.

In der neuen V8-Laufzeit entspricht das Schlüsselwort const dem Standard. Wenn Sie einer als const deklarierten Variablen einen Wert zuweisen, führt dies zu einem TypeError: Assignment to constant variable-Laufzeitfehler.

Weisen Sie beim Migrieren Ihres Skripts zu V8 nicht den Wert einer const-Variablen neu zu:

// 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
      

XML-Literale und das XML-Objekt vermeiden

Mit dieser nicht standardmäßigen Erweiterung für ECMAScript kann in Apps Script-Projekten direkt die XML-Syntax verwendet werden.

Vermeiden Sie beim Migrieren Ihres Skripts zu V8 die Verwendung direkter XML-Literale oder des XML-Objekts.

Verwenden Sie stattdessen XmlService, um XML zu parsen:

// 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
      

Keine benutzerdefinierten Iteratorfunktionen mit __iterator__ erstellen

In JavaScript 1.7 wurde eine Funktion hinzugefügt, mit der jeder Klasse ein benutzerdefinierter Iterator hinzugefügt werden kann, indem eine __iterator__-Funktion im Prototyp der Klasse deklariert wird. Diese Funktion wurde auch in die Rhino-Laufzeit von Apps Script aufgenommen, um Entwicklern die Arbeit zu erleichtern. Diese Funktion war jedoch nie Teil des ECMA-262-Standards und wurde in ECMAScript-kompatiblen JavaScript-Engines entfernt. Bei Skripts, die V8 verwenden, kann dieser Iterator nicht erstellt werden.

Vermeiden Sie beim Migrieren Ihres Skripts zu V8 die Funktion __iterator__, um benutzerdefinierte Iteratoren zu erstellen. Verwenden Sie stattdessen ECMAScript 6-Iteratoren.

Sehen Sie sich die folgende Array-Erstellung an:

// 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.
      

Die folgenden Codebeispiele zeigen, wie ein Iterator in der Rhino-Laufzeit erstellt werden kann und wie ein Ersatz-Iterator in der V8-Laufzeit erstellt wird:

// 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);
}
      

Bedingte Catch-Klauseln vermeiden

Die V8-Laufzeit unterstützt keine catch..if-bedingten Catch-Klauseln, da sie nicht standardkonform sind.

Beim Migrieren Ihres Skripts zu V8 müssen Sie alle Catch-Bedingungen in den Catch-Body verschieben:

// 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
  }
}

Verwendung von Object.prototype.toSource() vermeiden

JavaScript 1.3 enthielt die Methode Object.prototype.toSource(), die nie Teil eines ECMAScript-Standards war. Sie wird in der V8-Laufzeit nicht unterstützt.

Wenn Sie Ihr Skript zu V8 migrieren, entfernen Sie alle Verwendungen von Object.prototype.toSource() aus Ihrem Code.

Weitere Unterschiede

Zusätzlich zu den oben genannten Inkompatibilitäten, die zu Scriptfehlern führen können, gibt es einige weitere Unterschiede, die, wenn sie nicht korrigiert werden, zu unerwartetem Scriptverhalten in der V8-Laufzeit führen können.

In den folgenden Abschnitten wird erläutert, wie Sie Ihren Skriptcode aktualisieren können, um diese unerwarteten Überraschungen zu vermeiden.

Länderspezifische Datums- und Uhrzeitformatierung anpassen

Die Methoden Date, toLocaleString(), toLocaleDateString() und toLocaleTimeString() verhalten sich in der V8-Laufzeitumgebung anders als in Rhino.

In Rhino ist das Standardformat das Langformat und alle übergebenen Parameter werden ignoriert.

In der V8-Laufzeit ist das Standardformat das kurze Format und übergebene Parameter werden gemäß dem ECMA-Standard verarbeitet (weitere Informationen finden Sie in der toLocaleDateString()-Dokumentation).

Wenn Sie Ihr Script zu V8 migrieren, testen und passen Sie die Erwartungen Ihres Codes bezüglich der Ausgabe von gebietsschemaspezifischen Datums- und Zeitmethoden an.

// 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' }));
      

Error.fileName und Error.lineNumber vermeiden

In der V8-Laufzeitumgebung unterstützt das Standard-JavaScript-Objekt Error nicht die fileName- oder lineNumber-Parameter als Konstruktorparameter oder Objekteigenschaften.

Wenn Sie Ihr Skript zu V8 migrieren, entfernen Sie alle Abhängigkeiten von Error.fileName und Error.lineNumber.

Alternativ können Sie Error.prototype.stack verwenden. Dieser Stack ist ebenfalls nicht standardmäßig, wird aber in V8 unterstützt. Das Format des von den beiden Plattformen erstellten Stacktrace unterscheidet sich geringfügig:

// 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)
      

Umgang mit in Strings umgewandelten Enum-Objekten anpassen

In der ursprünglichen Rhino-Laufzeit wird bei Verwendung der JavaScript-Methode JSON.stringify() für ein Enum-Objekt nur {} zurückgegeben.

In V8 wird bei Verwendung derselben Methode für ein Enum-Objekt der Enum-Name zurückgegeben.

Beim Migrieren Ihres Skripts zu V8 sollten Sie die Erwartungen Ihres Codes bezüglich der Ausgabe von JSON.stringify() für Enum-Objekte testen und anpassen.

// 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"

Handhabung nicht definierter Parameter anpassen

In der ursprünglichen Rhino-Laufzeit wurde beim Übergeben von undefined an eine Methode als Parameter der String "undefined" an diese Methode übergeben.

In V8 entspricht die Übergabe von undefined an Methoden der Übergabe von null.

Beim Migrieren Ihres Skripts zu V8 sollten Sie die Erwartungen Ihres Codes in Bezug auf undefined-Parameter testen und anpassen:

// 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.

Umgang mit globalen this anpassen

Die Rhino-Laufzeit definiert einen impliziten speziellen Kontext für Skripts, die sie verwenden. Scriptcode wird in diesem impliziten Kontext ausgeführt, der sich vom tatsächlichen globalen this unterscheidet. Das bedeutet, dass sich Verweise auf die „globale this“ im Code tatsächlich auf den speziellen Kontext beziehen, der nur den im Script definierten Code und die Variablen enthält. Die integrierten Apps Script-Dienste und ECMAScript-Objekte sind von dieser Verwendung von this ausgeschlossen. Diese Situation ähnelte der folgenden JavaScript-Struktur:

// 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 wird der implizite spezielle Kontext entfernt. Globale Variablen und Funktionen, die im Skript definiert sind, werden neben den integrierten Apps Script-Diensten und ECMAScript-Funktionen wie Math und Date in den globalen Kontext eingefügt.

Wenn Sie Ihr Skript zu V8 migrieren, testen und passen Sie die Erwartungen Ihres Codes bezüglich der Verwendung von this in einem globalen Kontext an. In den meisten Fällen sind die Unterschiede nur dann sichtbar, wenn in Ihrem Code die Schlüssel oder Eigenschaftsnamen des globalen this-Objekts untersucht werden:

// 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));
}

Umgang mit instanceof in Bibliotheken anpassen

Wenn Sie instanceof in einer Bibliothek für ein Objekt verwenden, das als Parameter in einer Funktion aus einem anderen Projekt übergeben wird, kann es zu falsch negativen Ergebnissen kommen. In der V8-Laufzeit werden ein Projekt und seine Bibliotheken in verschiedenen Ausführungskontexten ausgeführt und haben daher unterschiedliche globale Variablen und Prototypketten.

Das ist nur der Fall, wenn in Ihrer Bibliothek instanceof für ein Objekt verwendet wird, das nicht in Ihrem Projekt erstellt wurde. Die Verwendung für ein Objekt, das in Ihrem Projekt erstellt wurde, sollte wie erwartet funktionieren, unabhängig davon, ob es sich um dasselbe oder ein anderes Skript in Ihrem Projekt handelt.

Wenn ein Projekt, das auf V8 ausgeführt wird, Ihr Skript als Bibliothek verwendet, prüfen Sie, ob in Ihrem Skript instanceof für einen Parameter verwendet wird, der aus einem anderen Projekt übergeben wird. Passen Sie die Verwendung von instanceof an und verwenden Sie je nach Anwendungsfall andere geeignete Alternativen.

Eine Alternative für a instanceof b kann der Konstruktor von a sein, wenn Sie nicht die gesamte Prototypkette durchsuchen müssen, sondern nur den Konstruktor prüfen möchten. Verwendung: a.constructor.name == "b"

Angenommen, Projekt A verwendet Projekt B als Bibliothek.

//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
}

Eine weitere Alternative besteht darin, eine Funktion einzuführen, die instanceof im Hauptprojekt prüft, und die Funktion zusätzlich zu anderen Parametern beim Aufrufen einer Bibliotheksfunktion zu übergeben. Die übergebene Funktion kann dann verwendet werden, um instanceof in der Bibliothek zu prüfen.

//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);
}
      

Übergabe von nicht gemeinsam genutzten Ressourcen an Bibliotheken anpassen

Die Übergabe einer nicht freigegebenen Ressource vom Hauptskript an eine Bibliothek funktioniert in der V8‑Laufzeit anders.

In der Rhino-Laufzeitumgebung funktioniert das Übergeben einer nicht freigegebenen Ressource nicht. Die Bibliothek verwendet stattdessen eine eigene Ressource.

In der V8-Laufzeit funktioniert die Übergabe einer nicht freigegebenen Ressource an die Bibliothek. Die Bibliothek verwendet die übergebene nicht freigegebene Ressource.

Übergeben Sie keine nicht freigegebenen Ressourcen als Funktionsparameter. Deklarieren Sie nicht freigegebene Ressourcen immer in demselben Skript, in dem sie verwendet werden.

Angenommen, Projekt A verwendet Projekt B als Bibliothek. In diesem Beispiel ist PropertiesService eine nicht freigegebene Ressource.

// Rhino runtime
// Project A
function testPassingNonSharedProperties() {
  PropertiesService.getScriptProperties()
      .setProperty('project', 'Project-A');
  B.setScriptProperties();
  // Prints: Project-B
  Logger.log(B.getScriptProperties(
      PropertiesService, 'project'));
}

//Project B function setScriptProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-B'); } function getScriptProperties( propertiesService, key) { return propertiesService.getScriptProperties() .getProperty(key); }

// V8 runtime
// Project A
function testPassingNonSharedProperties() {
  PropertiesService.getScriptProperties()
      .setProperty('project', 'Project-A');
  B.setScriptProperties();
  // Prints: Project-A
  Logger.log(B.getScriptProperties(
      PropertiesService, 'project'));
}

// Project B function setProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-B'); } function getScriptProperties( propertiesService, key) { return propertiesService.getScriptProperties() .getProperty(key); }

Zugriff auf eigenständige Skripts aktualisieren

Bei eigenständigen Skripts, die in der V8-Laufzeit ausgeführt werden, müssen Sie Nutzern mindestens Lesezugriff auf das Skript gewähren, damit die Trigger des Skripts richtig funktionieren.