Jeśli masz istniejący skrypt używający środowiska wykonawczego Rhino i chcesz użyć ze składni i funkcji V8, musisz przenieść skrypt do wersji 8.
Większość skryptów napisanych w środowisku wykonawczym Rhino może działać w środowisku V8 bez regulacji. Często jedynym wymogiem wstępnym po dodaniu składni V8 cech skryptu włączenie środowiska wykonawczego V8.
Istnieje jednak niewielki zestaw niezgodności i inne różnice, które mogą spowodować, że skrypt jeśli po włączeniu środowiska wykonawczego V8 występują nieoczekiwane błędy lub działa nieoczekiwanie. W trakcie migracji skrypt do obsługi V8, musisz poszukać tych problemów w projekcie skryptu i poprawić wszystkie znalezione błędy.
Procedura migracji V8
Aby przenieść skrypt do wersji 8, wykonaj te czynności:
- Włączanie środowiska wykonawczego V8 dla skryptu.
- Dokładnie zapoznaj się z niezgodnościami. wymienionych poniżej. Przeanalizuj swój skrypt, aby określić, czy którykolwiek z występują niezgodności; jeśli występuje co najmniej jedna niezgodność, dostosować kod skryptu, aby go usunąć lub uniknąć problemu.
- Uważnie zapoznaj się z pozostałymi różnicami wymienionymi poniżej. Sprawdź skrypt, aby określić, czy któraś z wymienionych różnic ma wpływ działania kodu. Dostosuj skrypt, aby rozwiązać ten problem.
- Po wyeliminowaniu wszelkich wykrytych niezgodności różnic, możesz zaktualizować kod, aby użyć Składnia V8 i inne funkcje zgodnie z potrzebami.
- Po wprowadzeniu poprawek w kodzie dokładnie przetestuj skrypt, aby zapewnić aby upewnić się, że działa zgodnie z oczekiwaniami.
- Jeśli skrypt jest aplikacją internetową lub opublikowanym dodatkiem, musisz utwórz nową wersję z korektami V8. Aby udostępnić wersję V8 , musisz ponownie opublikować skrypt z tą wersją.
Niezgodności
Oryginalne środowisko wykonawcze Apps Script oparte na Rhino zezwalało na niestandardowe zachowania ECMAScript. Ponieważ V8 jest zgodne ze standardami, nie są obsługiwane po migracji. Nierozwiązane problemy włączenie środowiska wykonawczego V8 powoduje błędy lub uszkodzenie skryptu.
W poniższych sekcjach opisano wszystkie te zachowania i czynności, które musisz podjąć , aby poprawić kod skryptu podczas migracji do wersji 8.
Unikaj: for each(variable in object)
for each (variable in object)
została dodana do JavaScript w wersji 1.6 i usunięta na rzecz for...of
.
Podczas przenoszenia skryptu do wersji 8 unikaj używania for each (variable in object)
wyciągów.
Zamiast niego użyj zasady 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 Rhino
Date.prototype.getYear()
zwraca lata 2-cyfrowe dla lat 1900-1999, ale czterocyfrowych lat w przypadku pozostałych
co działało w JavaScripcie 1.2 i starszych wersjach.
W środowisku wykonawczym V8
Date.prototype.getYear()
zwraca rok minus 1900 zgodnie z wymaganym przez
standardów ECMAScript.
Przy migracji skryptu do wersji 8 zawsze używaj
Date.prototype.getFullYear()
,
, który zwraca czterocyfrowy rok niezależnie od daty.
Unikaj używania zarezerwowanych słów kluczowych jako nazw
ECMAScript zabrania stosowania określonych zarezerwowanych słów kluczowych, w nazwach funkcji i zmiennych. Środowisko wykonawcze Rhino zezwalało na 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 wersji 8 unikaj nazywania zmiennych i funkcji
za pomocą jednej z
zarezerwowanych słów kluczowych.
Zmień nazwy zmiennych lub funkcji, aby nie używać nazwy słowa kluczowego. Częste zastosowania
słów kluczowych jako nazw to class
, import
i export
.
Unikaj ponownego przypisywania const
zmiennych
W pierwotnym środowisku Rhino możesz zadeklarować zmienną za pomocą atrybutu const
, który
oznacza, że wartość symbolu nigdy się nie zmienia i przyszłe przypisania do
są ignorowane.
W nowym środowisku wykonawczym V8 słowo kluczowe const
jest zgodne ze standardem i przypisuje
do zmiennej zadeklarowanej jako const
skutkuje
Błąd środowiska wykonawczego TypeError: Assignment to constant variable
.
Podczas migracji skryptu do wersji 8 nie próbuj ponownie przypisywać wartości
zmienną 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
Ten niestandardowe rozszerzenie do ECMAScript pozwala projektom Apps Script bezpośrednio używać składni XML.
Podczas migracji skryptu do wersji 8 unikaj używania bezpośrednich literałów XML oraz kodu XML .
Zamiast tego użyj XmlService do przeanalizuj 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ą __iterator__
W JavaScripcie 1.7 dodano funkcję umożliwiającą dodanie niestandardowego iteratora do każdej klamy
s przez zadeklarowanie funkcji __iterator__
w prototypie tej klasy; to było
dodaliśmy też do środowiska wykonawczego Apps Script Rhino, co jest udogodnieniem dla programistów. Pamiętaj jednak:
ta funkcja nigdy nie była częścią
Standard ECMA-262
i został usunięty w silnikach JavaScript zgodnych z ECMAScript. Skrypty korzystające z V8
nie można użyć tej konstrukcji iteratora.
Podczas migracji skryptu do wersji 8 unikaj funkcji __iterator__
przy kompilowaniu
niestandardowych iteratorów. Zamiast tego:
korzystać z iteratorów ECMAScript 6.
Rozważ taką konstrukcję macierzową:
// 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 skonstruować iterator w funkcji środowiska wykonawczego Rhino oraz instrukcje tworzenia iteratora zastępczego 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 warunkowych klauzul przechwytywania
Środowisko wykonawcze V8 nie obsługuje klauzul warunkowych typu catch..if
, ponieważ
nie są zgodne ze standardami.
Podczas migracji skryptu do wersji 8 przenieś wszystkie warunki przechwytywania do wewnątrz tagu [cat 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 } } |
Unikaj stosowania atrybutu Object.prototype.toSource()
JavaScript 1.3 zawierał Object.prototype.toSource() która nigdy nie była częścią żadnego standardu ECMAScript. Nie jest obsługiwane w tych krajach: w środowisku wykonawczym V8.
Podczas migracji skryptu do wersji 8 usuń wszelkie użycie Object.prototype.toSource() z kodu.
Inne różnice
Oprócz powyższych niezgodności, które mogą powodować błędy skryptów, występuje też to kilka innych różnic, które – jeśli nie zostaną skorygowane, mogą spowodować nieoczekiwany V8 działania skryptu środowiska wykonawczego.
W sekcjach poniżej wyjaśniamy, jak zaktualizować kod skryptu, by uniknąć takich sytuacji. nieoczekiwanych niespodzianek.
Dostosowywanie formatowania daty i godziny zależnie od języka
Date
metody toLocaleString()
,
toLocaleDateString()
,
i toLocaleTimeString()
działają
w środowisku wykonawczym V8 inaczej niż w Rhino.
W Rhino domyślnym formatem jest długi format, a wszystkie parametry przekazywane w są ignorowane.
W środowisku wykonawczym V8 domyślnym formatem jest krótki format i parametry
przekazywane są obsługiwane zgodnie ze standardem ECMA (zobacz
Dokumentacja toLocaleDateString()
).
Podczas przenoszenia skryptu do wersji 8 przetestuj i dostosuj swoje oczekiwania wobec kodu o wynikach metod daty i godziny specyficznych dla języka:
// 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 atrybutów Error.fileName
i Error.lineNumber
W czasie działania V8 standardowy kod JavaScript
Error
obiekt nie obsługuje fileName
ani lineNumber
jako parametrów konstruktora
lub właściwości obiektu.
Podczas migracji skryptu do wersji 8
usuń wszelkie zależności od Error.fileName
i Error.lineNumber
.
Alternatywnym rozwiązaniem jest użycie
Error.prototype.stack
Ten stos jest również niestandardowy, ale obsługiwany zarówno przez Rhino, jak i V8.
format zrzutu stosu wygenerowanego przez te 2 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) |
Dostosuj obsługę obiektów wyliczeniowych w postaci ciągu znaków
W pierwotnym środowisku wykonawczym Rhino za pomocą JavaScriptu
JSON.stringify()
w obiekcie wyliczenia zwraca tylko {}
.
W wersji 8 użycie tej samej metody w obiekcie wyliczenia powoduje zmianę nazwy wyliczenia.
Podczas migracji skryptu do wersji 8
przetestować i dostosować oczekiwania kodu dotyczące wyników
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 przekazywanie parametru undefined
do metody jako parametru.
zaowocowało przekazaniem ciągu "undefined"
do tej metody.
W wersji 8 przekazanie undefined
do metod jest równoważne z przekazywaniem wartości null
.
Podczas migracji skryptu do wersji 8
Sprawdź i dostosuj oczekiwania kodu związane z parametrami 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. |
Dostosuj obsługę globalnego zasobu this
Środowisko wykonawcze Rhino definiuje niejawny specjalny kontekst dla skryptów, które z niego korzystają.
Kod skryptu działa w tym niejawnym kontekście, inny niż rzeczywisty globalny
this
Oznacza to, że odwołania do atrybutu „this
” w kodzie
są analizowane w specjalnym kontekście, który zawiera tylko kod i zmienne
w skrypcie. wbudowane usługi Apps Script i obiekty ECMAScript;
nie można używać w tych usługach: this
. Sytuacja była podobna do tej
Struktura JavaScriptu:
// 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 V8 niejawny kontekst specjalny jest usuwany. Zmienne i funkcje globalne
zdefiniowane w skrypcie są umieszczane w kontekście globalnym, obok wbudowanych
Usługi Apps Script i wbudowane skrypty ECMAScript, np. Math
i Date
.
Podczas przenoszenia skryptu do wersji 8 przetestuj i dostosuj swoje oczekiwania wobec kodu
dotyczące używania 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
globalny obiekt 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)); } |
Dostosuj obsługę pliku instanceof
w bibliotekach
Użycie funkcji instanceof
w bibliotece w obiekcie przekazywanym jako parametr w
funkcja z innego projektu może dawać wyniki fałszywie negatywne. W środowisku wykonawczym V8
i jego biblioteki są uruchamiane w różnych kontekstach wykonania,
w różnych łańcuchach globalnych i prototypowych.
Pamiętaj, że dzieje się tak tylko wtedy, gdy biblioteka używa na obiekcie instanceof
który nie został utworzony w projekcie. Korzystanie z niego w obiekcie utworzonym w
w swoim projekcie – w tym samym lub innym skrypcie –
powinno działać zgodnie z oczekiwaniami.
Jeśli projekt uruchomiony w wersji 8 używa Twojego skryptu jako biblioteki, sprawdź, czy
skrypt używa parametru instanceof
, który zostanie przekazany z innego projektu. Dostosuj
korzystanie z usługi instanceof
i korzystanie z innych realnych alternatyw zgodnie z Twoimi potrzebami
Alternatywą dla a instanceof b
jest użycie konstruktora a
w których przypadku nie trzeba przeszukiwać całego łańcucha prototypów
za pomocą konstruktora.
Wykorzystanie: a.constructor.name == "b"
Weźmy pod uwagę Projekt A i Projekt B, w ramach którego Projekt A wykorzystuje Projekt B jako bibliotekę.
//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 } |
Inną możliwością jest wprowadzenie funkcji, która sprawdza instanceof
w głównym projekcie
i przekazują ją razem z innymi parametrami przy wywoływaniu funkcji biblioteki. Przekazana funkcja
można następnie użyć do sprawdzenia elementu 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); } |
Dostosuj przekazywanie nieudostępnionych zasobów do bibliotek
Przekazywanie nieudostępnionych zasobów z głównego skryptu do biblioteki działa inaczej w środowisku wykonawczym V8.
W środowisku wykonawczym Rhino przekazanie zasobów nieudostępnionych nie zadziała. Biblioteka używa własnych zasobów.
W środowisku wykonawczym V8 przesyłanie nieudostępnionych zasobów do biblioteki działa. Biblioteka korzysta z przekazanego nieudostępnionego zasobu.
Nie przekazuj nieudostępnionych zasobów jako parametrów funkcji. Zasoby nieudostępnione zawsze należy deklarować w tym samym skrypcie, który ich używa.
Weźmy pod uwagę Projekt A i Projekt B, w ramach którego Projekt A wykorzystuje Projekt B jako bibliotekę. W tym przykładzie zasób PropertiesService
nie jest udostępniany.
// 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')); } |
Zaktualizuj dostęp do samodzielnych skryptów
W przypadku samodzielnych skryptów działających w środowisku wykonawczym V8 musisz zapewnić użytkownikom co najmniej wyświetlanie dostępu do skryptu, aby zapewnić prawidłowe działanie wyzwalaczy skryptu.