Środowisko wykonawcze Rhino zostanie wyłączone 31 stycznia 2026 r. lub później. Jeśli masz skrypt korzystający ze środowiska wykonawczego Rhino, musisz przenieść go do środowiska V8.
Często jedynym warunkiem wstępnym dodania do skryptu składni i funkcji V8 jest włączenie środowiska wykonawczego V8. Istnieje jednak niewielki zestaw niezgodności i innych różnic, które mogą spowodować, że skrypt nie będzie działać lub będzie zachowywać się nieoczekiwanie w środowisku wykonawczym V8. Podczas przenoszenia skryptu do V8 musisz wyszukać w projekcie skryptu te problemy i je rozwiązać.
Procedura migracji do V8
Aby przenieść skrypt do V8, wykonaj te czynności:
- Włącz środowisko wykonawcze V8 w skrypcie.
runtimeVersion
można sprawdzić w pliku manifestu projektu Apps Script. - Uważnie zapoznaj się z poniższymi niezgodnościami. Sprawdź skrypt, aby ustalić, czy występują w nim jakiekolwiek niezgodności. Jeśli tak, zmodyfikuj kod skryptu, aby usunąć problem lub go uniknąć.
- Uważnie zapoznaj się z innymi różnicami. Sprawdź skrypt, aby określić, czy któraś z wymienionych różnic wpływa na działanie kodu. Dostosuj skrypt, aby poprawić jego działanie.
- Po usunięciu wszystkich wykrytych niezgodności lub innych różnic możesz zacząć aktualizować kod, aby używać składni V8 i innych funkcji.
- Po wprowadzeniu zmian w kodzie dokładnie przetestuj skrypt, aby upewnić się, że działa zgodnie z oczekiwaniami.
- Jeśli Twój skrypt jest aplikacją internetową lub opublikowanym dodatkiem, musisz utworzyć nową wersję skryptu z dostosowaniami do V8 i skierować wdrożenie na nowo utworzoną wersję. Aby udostępnić użytkownikom wersję V8, musisz ponownie opublikować skrypt z tą wersją.
- Jeśli skrypt jest używany jako biblioteka, utwórz nowe wdrożenie skryptu z określoną wersją. Poinformuj wszystkie skrypty i użytkowników korzystających z Twojej biblioteki o nowej wersji i poproś ich o zaktualizowanie jej do wersji obsługującej V8. Sprawdź, czy starsze wersje biblioteki oparte na Rhino nie są już aktywnie używane ani dostępne.
- Sprawdź, czy żadne instancje skryptu nie działają jeszcze w starszym środowisku wykonawczym Rhino. Sprawdź, czy wszystkie wdrożenia są powiązane z wersją, która jest w wersji 8. Archiwizuj stare wdrożenia. Sprawdź wszystkie wersje i usuń te, które nie korzystają ze środowiska wykonawczego V8.
Niezgodności
Oryginalne środowisko wykonawcze Apps Script oparte na Rhino niestety dopuszczało kilka niestandardowych zachowań ECMAScript. V8 jest zgodny ze standardami, więc po migracji te zachowania nie są obsługiwane. Niepoprawienie tych problemów spowoduje błędy lub nieprawidłowe działanie skryptu po włączeniu środowiska wykonawczego V8.
W sekcjach poniżej znajdziesz opis każdego z tych zachowań oraz czynności, które musisz wykonać, aby poprawić kod skryptu podczas migracji do V8.
Unikaj for each(variable in object)
Instrukcja for each (variable in object)
została dodana w JavaScript 1.6, a następnie usunięta na rzecz for...of
.
Podczas przenoszenia skryptu do V8 unikaj używania for each (variable in object)
instrukcji.
Zamiast tego użyj tego kodu: 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); } |
Unikaj Date.prototype.getYear()
W oryginalnym środowisku wykonawczym Rhino funkcja
Date.prototype.getYear()
zwraca dwucyfrowe lata w przypadku lat 1900–1999, ale czterocyfrowe lata w przypadku innych dat, co było zachowaniem w JavaScript 1.2 i starszych wersjach.
W środowisku wykonawczym V8 funkcja
Date.prototype.getYear()
zwraca rok pomniejszony o 1900, zgodnie z wymaganiami
standardów ECMAScript.
Podczas przenoszenia skryptu do V8 zawsze używaj funkcji
Date.prototype.getFullYear()
,
która zwraca czterocyfrowy rok niezależnie od daty.
Unikaj używania zarezerwowanych słów kluczowych jako nazw
ECMAScript zabrania używania niektórych słów kluczowych w nazwach funkcji i zmiennych. Środowisko wykonawcze Rhino dopuszczało wiele z tych słów, więc jeśli Twój kod ich używa, musisz zmienić nazwy funkcji lub zmiennych.
Podczas przenoszenia skryptu do V8 unikaj nazywania zmiennych lub funkcji za pomocą jednego z zarezerwowanych słów kluczowych.
Zmień nazwę dowolnej zmiennej lub funkcji, aby nie używać nazwy słowa kluczowego. Słowa kluczowe są często używane jako nazwy, np. class
, import
i export
.
Unikaj ponownego przypisywania zmiennych const
W oryginalnym środowisku wykonawczym Rhino możesz zadeklarować zmienną za pomocą słowa kluczowego const
, co oznacza, że wartość symbolu nigdy się nie zmienia, a przyszłe przypisania do symbolu są ignorowane.
W nowym środowisku wykonawczym V8 słowo kluczowe const
jest zgodne ze standardem, a przypisanie do zmiennej zadeklarowanej jako const
powoduje błąd środowiska wykonawczego TypeError: Assignment to constant variable
.
Podczas przenoszenia skryptu do V8 nie próbuj ponownie przypisywać wartości zmiennej 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 |
Unikaj literałów XML i obiektu XML
To niestandardowe rozszerzenie ECMAScriptu umożliwia projektom Apps Script bezpośrednie używanie składni XML.
Podczas przenoszenia skryptu do V8 unikaj używania bezpośrednich literałów XML lub obiektu XML.
Zamiast tego użyj XmlService do analizowania kodu 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 |
Nie twórz niestandardowych funkcji iteratora za pomocą funkcji __iterator__
.
W JavaScript 1.7 dodano funkcję umożliwiającą dodanie niestandardowego iteratora do dowolnej klasy przez zadeklarowanie funkcji __iterator__
w prototypie tej klasy. Została ona również dodana do środowiska wykonawczego Rhino w Apps Script, aby ułatwić pracę programistom. Ta funkcja nigdy nie była jednak częścią standardu ECMA-262 i została usunięta z silników JavaScript zgodnych z ECMAScript. Skrypty korzystające z V8 nie mogą używać tej konstrukcji iteratora.
Podczas przenoszenia skryptu do V8 unikaj funkcji __iterator__
, aby tworzyć niestandardowe iteratory. Zamiast tego używaj iteratorów ECMAScript 6.
Rozważ następującą konstrukcję tablicy:
// 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. |
Poniższe przykłady kodu pokazują, jak można utworzyć iterator w środowisku wykonawczym Rhino i jak utworzyć iterator zastępczy w środowisku wykonawczym 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); } |
Unikaj klauzul warunkowych catch
Środowisko wykonawcze V8 nie obsługuje klauzul warunkowych catch..if
, ponieważ nie są one zgodne ze standardem.
Podczas przenoszenia skryptu do V8 przenieś wszystkie warunki catch do treści bloku 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 } } |
Unikaj używania Object.prototype.toSource()
JavaScript 1.3 zawierał metodę Object.prototype.toSource(), która nigdy nie była częścią żadnego standardu ECMAScript. Nie jest obsługiwana w środowisku wykonawczym V8.
Podczas przenoszenia skryptu do V8 usuń z kodu wszystkie wystąpienia funkcji Object.prototype.toSource().
Inne różnice
Oprócz wymienionych wyżej niezgodności, które mogą powodować błędy skryptu, istnieje kilka innych różnic, które, jeśli nie zostaną skorygowane, mogą skutkować nieoczekiwanym działaniem skryptu w środowisku wykonawczym V8.
W kolejnych sekcjach wyjaśniamy, jak zaktualizować kod skryptu, aby uniknąć tych niespodzianek.
Dostosowywanie formatowania daty i godziny do ustawień regionalnych
Metody Date
, toLocaleString()
, toLocaleDateString()
i toLocaleTimeString()
działają inaczej w środowisku wykonawczym V8 niż w Rhino.
W Rhino domyślnym formatem jest format długi, a wszystkie przekazywane parametry są ignorowane.
W środowisku wykonawczym V8 domyślnym formatem jest krótki format, a przekazywane parametry są obsługiwane zgodnie ze standardem ECMA (szczegóły znajdziesz w toLocaleDateString()
dokumentacji).
Podczas przenoszenia skryptu do V8 przetestuj i dostosuj oczekiwania kodu dotyczące danych wyjściowych metod daty i godziny specyficznych dla danego regionu:
// 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' })); |
Unikaj używania Error.fileName
i Error.lineNumber
W środowisku wykonawczym V8 standardowy obiekt JavaScriptError
nie obsługuje znaków fileName
ani lineNumber
jako parametrów konstruktora ani właściwości obiektu.
Podczas migracji skryptu do V8 usuń wszystkie zależności od Error.fileName
i Error.lineNumber
.
Możesz też użyć polecenia Error.prototype.stack
.
Ten stos jest również niestandardowy, ale obsługiwany w V8. Format śladu stosu generowanego przez obie platformy jest nieco inny:
// 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) |
Dostosowywanie obsługi obiektów wyliczeniowych w formie ciągu znaków
W oryginalnym środowisku wykonawczym Rhino użycie metody JavaScript JSON.stringify()
na obiekcie wyliczeniowym zwraca tylko wartość {}
.
W V8 użycie tej samej metody w przypadku obiektu wyliczeniowego zwraca nazwę wyliczenia.
Podczas przenoszenia skryptu do V8 przetestuj i dostosuj oczekiwania kodu dotyczące wyniku działania funkcji JSON.stringify()
na obiektach wyliczeniowych:
// 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" |
Dostosowywanie obsługi niezdefiniowanych parametrów
W pierwotnym środowisku wykonawczym Rhino przekazanie do metody wartości undefined
jako parametru powodowało przekazanie do tej metody ciągu znaków "undefined"
.
W V8 przekazywanie wartości undefined
do metod jest równoznaczne z przekazywaniem wartości null
.
Podczas migracji skryptu do V8 przetestuj i dostosuj oczekiwania kodu dotyczące undefined
parametrów:
// 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. |
Dostosowywanie obsługi globalnego elementu this
Środowisko wykonawcze Rhino definiuje niejawny kontekst specjalny dla skryptów, które go używają.
Kod skryptu jest uruchamiany w tym niejawnym kontekście, który różni się od rzeczywistego obiektu globalnegothis
. Oznacza to, że odwołania do „globalnego this
” w kodzie są w rzeczywistości interpretowane jako specjalny kontekst, który zawiera tylko kod i zmienne zdefiniowane w skrypcie. Wbudowane usługi Apps Script i obiekty ECMAScript są wykluczone z tego zastosowania this
. Sytuacja była podobna do tej struktury 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. }(); |
W wersji 8 usunięto domyślny kontekst specjalny. Zmienne globalne i funkcje zdefiniowane w skrypcie są umieszczane w kontekście globalnym obok wbudowanych usług Apps Script i wbudowanych funkcji ECMAScript, takich jak Math
i Date
.
Podczas przenoszenia skryptu do V8 przetestuj i dostosuj oczekiwania kodu dotyczące użycia this
w kontekście globalnym. W większości przypadków różnice są widoczne tylko wtedy, gdy kod sprawdza klucze lub nazwy właściwości globalnego obiektu 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)); } |
Dostosowywanie obsługi instanceof
w bibliotekach
Użycie instanceof
w bibliotece na obiekcie przekazywanym jako parametr w funkcji z innego projektu może dać fałszywie ujemne wyniki. W środowisku wykonawczym V8 projekt i jego biblioteki działają w różnych kontekstach wykonania, a więc mają różne zmienne globalne i łańcuchy prototypów.
Pamiętaj, że ma to miejsce tylko wtedy, gdy biblioteka używa instanceof
na obiekcie, który nie został utworzony w Twoim projekcie. Użycie go w obiekcie utworzonym w Twoim projekcie, niezależnie od tego, czy znajduje się w tym samym, czy w innym skrypcie w projekcie, powinno działać zgodnie z oczekiwaniami.
Jeśli projekt działający na V8 używa Twojego skryptu jako biblioteki, sprawdź, czy skrypt używa funkcji instanceof
w przypadku parametru, który będzie przekazywany z innego projektu. Dostosuj użycie instanceof
i skorzystaj z innych możliwych alternatyw w zależności od przypadku użycia.
Alternatywą dla a instanceof b
może być użycie konstruktora a
w przypadkach, gdy nie musisz przeszukiwać całego łańcucha prototypów i wystarczy sprawdzić konstruktor.
Użycie: a.constructor.name == "b"
Załóżmy, że masz projekt A i projekt B, przy czym projekt A używa projektu B jako biblioteki.
//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 } |
Innym rozwiązaniem może być wprowadzenie funkcji, która sprawdza instanceof
w projekcie głównym, i przekazywanie jej oprócz innych parametrów podczas wywoływania funkcji biblioteki. Przekazaną funkcję można następnie wykorzystać do sprawdzenia instanceof
w bibliotece.
//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); } |
Dostosowywanie przekazywania do bibliotek zasobów nieudostępnionych
Przekazywanie nieudostępnionego zasobu z głównego skryptu do biblioteki działa inaczej w środowisku wykonawczym V8.
W środowisku wykonawczym Rhino przekazywanie zasobu nieudostępnionego nie będzie działać. Biblioteka używa własnego zasobu.
W środowisku wykonawczym V8 przekazywanie do biblioteki zasobu nieudostępnionego działa. Biblioteka używa przekazanego zasobu nieudostępnionego.
Nie przekazuj zasobów nieudostępnionych jako parametrów funkcji. Zawsze deklaruj zasoby nieudostępnione w tym samym skrypcie, który ich używa.
Załóżmy, że masz projekt A i projekt B, przy czym projekt A używa projektu B jako biblioteki. W tym przykładzie PropertiesService
to zasób nieudostępniony.
// 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')); } |
Aktualizowanie dostępu do samodzielnych skryptów
W przypadku samodzielnych skryptów działających w środowisku wykonawczym V8 musisz przyznać użytkownikom co najmniej dostęp do skryptu, aby wyzwalacze skryptu działały prawidłowo.