Skripts zur V8-Laufzeit migrieren

Wenn Sie bereits ein Skript mit der Rhino-Laufzeit haben, von V8-Syntax und -Funktionen müssen Sie das Skript zu V8 migrieren.

Die meisten Skripts, die mit der Rhino-Laufzeit geschrieben wurden, können mit einer V8-Laufzeit ausgeführt werden ohne Anpassung. Oft die einzige Voraussetzung, um V8-Syntax und einem Skript hinzuzufügen ist, Aktivieren der V8-Laufzeit

Es gibt jedoch einige Inkompatibilitäten und andere Unterschiede, die dazu führen können, dass ein Script nach der Aktivierung der V8-Laufzeit fehlschlägt oder sich unerwartet verhält. Während der Migration ein Script zur Verwendung von V8 erstellen, müssen Sie das Skriptprojekt auf diese Probleme und korrigieren Sie sie.

V8-Migrationsverfahren

Gehen Sie wie folgt vor, um ein Skript zu V8 zu migrieren:

  1. V8-Laufzeit aktivieren für das Skript.
  2. Lesen Sie sich die unten aufgeführten Inkompatibilitäten sorgfältig durch. Prüfen Sie Ihr Script, um festzustellen, ob eine oder mehrere der Inkompatibilitäten vorliegen. Falls ja, passen Sie den Scriptcode an, um das Problem zu beheben oder zu vermeiden.
  3. Prüfen Sie sorgfältig die anderen Unterschiede, die unten aufgeführt sind. Überprüfen Sie Ihr Skript, um festzustellen, ob sich einer der aufgeführten Unterschiede auf auf das Verhalten Ihres Codes. Passen Sie das Skript an, um das Verhalten zu korrigieren.
  4. Sobald Sie alle gefundenen Inkompatibilitäten oder anderen Unterschiede korrigiert haben, können Sie Ihren Code so aktualisieren, dass er die V8-Syntax und andere Funktionen verwendet.
  5. Testen Sie nach Abschluss Ihrer Codeanpassungen Ihr Skript gründlich, dass es wie erwartet funktioniert.
  6. Wenn es sich bei Ihrem Skript um eine Web-App oder ein veröffentlichtes Add-on handelt: Du musst neue Version erstellen des Skripts mit den V8-Anpassungen. Um die V8-Version für alle müssen Sie das Skript mit dieser Version erneut veröffentlichen.

Inkompatibilitäten

Die ursprüngliche Rhino-basierte Apps Script-Laufzeit erlaubte leider nicht standardmäßige ECMAScript-Verhaltensweisen. Da V8 standardkonform ist, sind diese werden nach der Migration nicht mehr unterstützt. Fehler beim Versäumen, diese Probleme zu beheben nach der Aktivierung der V8-Laufzeit zu Fehlern oder einem fehlerhaften Skriptverhalten führt.

In den folgenden Abschnitten werden diese Verhaltensweisen und 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 Scripts 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-Laufzeit Date.prototype.getYear() gibt zweistellige Jahre für die Jahre von 1900 bis 1999 und vierstellige Jahre für andere Datumsangaben, wie es in JavaScript 1.2 und früheren Versionen der Fall war.

In der V8-Laufzeit Date.prototype.getYear() gibt stattdessen das Jahr minus 1900 zurück, wie von ECMAScript-Standards.

Verwenden Sie bei der Migration Ihres Scripts zu V8 Date.prototype.getFullYear(), gibt unabhängig vom Datum ein vierstelliges Jahr zurück.

Reservierte Keywords nicht als Namen verwenden

ECMAScript verbietet die Verwendung bestimmter reservierte Keywords in Funktions- und Variablennamen. Die Rhino-Laufzeit erlaubte viele dieser Wörter, Wenn sie in Ihrem Code verwendet werden, müssen Sie daher die Funktionen oder Variablen umbenennen.

Vermeiden Sie bei der Migration Ihres Skripts zu V8 die Benennung von Variablen oder Funktionen mit einer der reservierten Keywords. Benennen Sie eine Variable oder Funktion um, damit der Keyword-Name nicht verwendet wird. Häufige Anwendungsbereiche der Keywords als Namen sind class, import und export.

Vermeiden Sie es, const-Variablen neu zuzuweisen

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 ist das Schlüsselwort const standardkonform und weist einer Variable hinzugefügt, die als const deklariert ist, führt zu einem TypeError: Assignment to constant variable Laufzeitfehler.

Versuchen Sie bei der Migration Ihres Skripts zu V8 nicht, den Wert von 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
      

XML-Literale und XML-Objekt vermeiden

Dieses nicht standardmäßige Erweiterung zu ECMAScript ermöglicht Apps Script-Projekten die direkte Verwendung der XML-Syntax.

Vermeiden Sie bei der Migration Ihres Skripts zu V8 die Verwendung von direkten XML-Literalen oder XML Objekt.

Verwenden Sie stattdessen den XmlService, um XML 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 Iterator-Funktionen mit __iterator__ erstellen

In JavaScript 1.7 wurde eine Funktion hinzugefügt, mit der ein benutzerdefinierter Iterator zu beliebigen Clas hinzugefügt werden kann. durch Deklarieren einer __iterator__-Funktion im Prototyp dieser Klasse. das war wird auch in die Rhino-Laufzeit von Apps Script aufgenommen, damit die App für Entwickler einfacher wird. Sie können jedoch war diese Funktion nie Teil des ECMA-262-Standard und wurde in ECMAScript-kompatiblen JavaScript-Engines entfernt. Bei Scripts mit V8 kann diese Iteratorkonstruktion nicht verwendet werden.

Vermeiden Sie bei der Migration Ihres Skripts zu V8 die Funktion __iterator__, benutzerdefinierte Iterationen. Stattdessen Verwenden Sie ECMAScript 6-Iteratoren.

Betrachten Sie die folgende Array-Konstruktion:

// 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 im Rhino-Laufzeit und wie Sie einen Ersatz-Iterator in der V8-Laufzeit erstellen:

// 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 bedingten Catch-catch..if-Klauseln, da sie nicht standardkonform sind.

Wenn Sie Ihr Skript zu V8 migrieren, verschieben Sie alle Catch-Bedingungen in den Catch-Body:

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

Object.prototype.toSource() vermeiden

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

Entfernen Sie bei der Migration Ihres Skripts zu V8 die Verwendung von Object.prototype.toSource() aus Ihrem Code.

Weitere Unterschiede

Neben den oben genannten Inkompatibilitäten, die zu Skriptfehlern führen können, können weitere Unterschiede sein, die, wenn sie nicht korrigiert werden, zu unerwarteten V8-Fehlern des Laufzeitskripts.

In den folgenden Abschnitten wird erläutert, wie Sie Ihren Skriptcode aktualisieren, um solche Probleme zu vermeiden. unerwartete Überraschungen.

Sprachspezifische Datums- und Uhrzeitformatierung anpassen

Die Date Methoden toLocaleString(), toLocaleDateString(), und toLocaleTimeString() in der V8-Laufzeit anders als bei Rhino.

In Rhino ist das Standardformat das long-Format. Alle Parameter, die in werden ignoriert.

In der V8-Laufzeit ist das Standardformat das Kurzformat und die Parameter übergeben werden, gemäß dem ECMA-Standard gehandhabt werden (siehe toLocaleDateString()-Dokumentation .

Wenn Sie Ihr Script zu V8 migrieren, testen und passen Sie die Erwartungen des Codes an. zur Ausgabe landesspezifischer Datums- und Uhrzeitmethoden:

// 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-Untime-Umgebung unterstützt das standardmäßige JavaScript-Objekt Error weder fileName noch lineNumber als Konstruktorparameter oder Objekteigenschaften.

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

Alternativ können Sie auch die Error.prototype.stack verwenden. Dieser Stack ist ebenfalls kein Standard, wird aber sowohl in Rhino als auch in V8 unterstützt. Die Format des von den beiden Plattformen erstellten Stacktrace ist etwas anders:

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

Handhabung von stringifizierten enum-Objekten anpassen

In der ursprünglichen Rhino-Laufzeit mithilfe des JavaScript- JSON.stringify() für ein Enum-Objekt nur {} zurück.

In V8 wird mit derselben Methode für ein Enum-Objekt der Name des Enumerations zurückgegeben.

Wenn Sie Ihr Skript zu V8 migrieren, Ihren Code hinsichtlich der Ausgabe des Codes JSON.stringify() zu enum-Objekten:

// 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 undefined als Parameter an eine Methode übergeben wurde der String "undefined" an diese Methode übergeben.

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

Wenn Sie Ihr Skript zu V8 migrieren, Testen Sie die undefined-Parameter und passen Sie sie entsprechend an:

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

Handhabung des globalen this anpassen

Die Rhino-Laufzeit definiert einen impliziten speziellen Kontext für Scripts, die sie verwenden. Der Skriptcode wird in diesem impliziten Kontext ausgeführt, und zwar getrennt vom tatsächlichen globalen Kontext. this Verweise auf den „globalen this“ im Code tatsächlich im speziellen Kontext auszuwerten, der nur den Code und die Variablen enthält die im Skript definiert sind. Integrierte Apps Script-Dienste und ECMAScript-Objekte sind von der Verwendung von this ausgeschlossen. Diese Situation sah in etwa so aus: 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 Spezialkontext entfernt. Globale Variablen und Funktionen im Skript definiert wurden, im globalen Kontext neben dem integrierten Apps Script-Dienste und integrierte ECMAScript-Anwendungen wie Math und Date

Wenn Sie Ihr Script zu V8 migrieren, testen und passen Sie die Erwartungen des Codes an. zur Verwendung von this in einem globalen Kontext. In den meisten Fällen sind die Unterschiede nur erkennbar, wenn in Ihrem Code die Schlüssel oder Property-Namen des globalen this-Objekts geprüft 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));
}

Verarbeitung von instanceof in Bibliotheken anpassen

Wenn Sie instanceof in einer Bibliothek auf einem Objekt verwenden, das als Parameter in einer Funktion aus einem anderen Projekt übergeben wird, kann es zu Falsch-Negativ-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 Prototyp-Ketten.

Beachten Sie, dass dies nur der Fall ist, wenn Ihre Bibliothek instanceof für ein Objekt verwendet. die nicht in Ihrem Projekt erstellt wird. Verwendung für ein Objekt, das in erstellt wird ob im selben oder in einem anderen Skript innerhalb Ihres Projekts, sollte wie erwartet funktionieren.

Wenn Ihr Script in einem Projekt verwendet wird, das unter V8 ausgeführt wird, prüfen Sie, ob in Ihrem Script instanceof für einen Parameter verwendet wird, der von einem anderen Projekt übergeben wird. Anpassen die Nutzung von instanceof und andere Alternativen entsprechend Ihrer Nutzung

Eine Alternative für a instanceof b kann die Verwendung des Konstruktors von a in in denen Sie nicht die gesamte Prototyp-Kette durchsuchen müssen, dem -Konstruktor. Nutzung: a.constructor.name == "b"

Nehmen wir als Beispiel Projekt A und Projekt B, bei dem Projekt A Projekt B als Bibliothek verwendet.

//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 übergibt. Mit der übergebenen Funktion kann dann instanceof in der Bibliothek geprüft werden.

//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 nicht freigegebener Ressourcen an Bibliotheken anpassen

Das Übergeben einer nicht freigegebenen Ressource vom Hauptskript an eine Bibliothek funktioniert in der V8-Laufzeit anders.

In der Rhino-Laufzeit funktioniert die Übergabe einer nicht freigegebenen Ressource nicht. Die Bibliothek verwendet stattdessen eine eigene Ressource.

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

Übergeben Sie nicht freigegebene Ressourcen nicht als Funktionsparameter. Deklarieren Sie nicht freigegebene Ressourcen immer in dem 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

Für eigenständige Skripts, die in der V8-Laufzeit ausgeführt werden, müssen Sie Nutzern mindestens Lesezugriff auf das Skript, damit die Auslöser des Skripts ordnungsgemäß funktionieren.