Rhino ランタイムを使用している既存のスクリプトで V8 構文と機能を使用したい場合は、スクリプトを V8 に移行する必要があります。
Rhino ランタイムを使用して記述されたスクリプトのほとんどは、V8 ランタイムを使用して動作できます 調整を加えずに測定できます多くの場合、V8 構文を追加するための前提条件は、 スクリプトに備わっている V8 ランタイムを有効にする
ただし、V8 ランタイムを有効にした後にスクリプトが失敗したり、予期しない動作をしたりする可能性がある、非互換性とその他の違いがいくつかあります。移行時 スクリプトを使用するには、スクリプト プロジェクトでこれらの問題を検索し、 是正してください。
V8 の移行手順
スクリプトを V8 に移行する手順は次のとおりです。
- V8 ランタイムを有効にする 使用します。
- 以下の互換性のない機能をよく確認してください。スクリプトを調べて、 互換性の問題があります。互換性の問題がある場合は スクリプト コードを調整して、問題を解消または回避できるようにしてください。
- 以下のその他の違いをよく確認してください。スクリプトを調べて、リストされている相違点の中に影響があるかどうかを確認します。 判断できます。スクリプトを調整して動作を修正します。
- 検出された非互換性やその他の違いを修正したら、必要に応じて V8 構文やその他の機能を使用するようにコードを更新できます。
- コードの調整が完了したら、スクリプトを入念にテストして、 確認します。
- スクリプトがウェブアプリや公開されているアドオンの場合、 必要があります 新しいバージョンを作成する 調整を加えた値が返されますV8 バージョンをユーザーが利用できるようにするには、このバージョンでスクリプトを再公開する必要があります。
非互換性
残念ながら、元の Rhino ベースの Apps Script ランタイムでは、 非標準の ECMAScript 動作には対応しません。V8 は標準に準拠しているため、移行後はこのような動作はサポートされません。これらの問題を修正できていない V8 ランタイムを有効にすると、エラーが発生したり、スクリプトの動作が壊れたりします。
以降のセクションでは、これらの動作のそれぞれと、V8 への移行中にスクリプトコードを修正するために必要な手順について説明します。
for each(variable in object)
を避ける
「
for each (variable in object)
ステートメントは JavaScript 1.6 に追加され、for...of
に置き換えられました。
スクリプトを V8 に移行する場合は、for each (variable in object)
は使用しないでください。
ステートメントをご覧ください。
代わりに 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); } |
Date.prototype.getYear()
を避ける
元の Rhino ランタイムでは、
Date.prototype.getYear()
1900 ~ 1999 年の場合は 2 桁の年を返し、それ以外の年の場合は 4 桁の年を返します。
これは JavaScript 1.2 以前では動作していました。
V8 ランタイムでは
Date.prototype.getYear()
は、代わりに年から 1900 を引いた値を返します。
ECMAScript 標準。
スクリプトを V8 に移行する場合は、必ず
Date.prototype.getFullYear()
,
これは、日付に関係なく 4 桁の年を返します。
予約済みキーワードを名前として使用しない
ECMAScript では、関数名と変数名に特定の予約済みキーワードを使用することは禁止されています。Rhino のランタイムでは そのため、コードでこれらの変数を使用している場合は、関数または変数の名前を変更する必要があります。
スクリプトを V8 に移行する場合は、変数や関数に名前を付けないようにする
いずれかを使用
予約済みキーワード。
キーワード名が使用されないように、変数または関数の名前を変更します。一般的な使用例
名前としてのキーワードの割合は、class
、import
、export
です。
const
変数の再代入を回避する
元の Rhino ランタイムでは、const
を使用して変数を宣言できます。
は、記号の値が変化せず、以降の割り当ても
記号は無視されます。
新しい V8 ランタイムでは、const
キーワードは標準に準拠しており、
const
として宣言された変数に割り当てると、
TypeError: Assignment to constant variable
ランタイム エラー。
スクリプトを V8 に移行する場合、
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 |
XML リテラルと XML オブジェクトは使用しない
この 非標準の拡張機能 ECMAScript への追加により、Apps Script プロジェクトで XML 構文を直接使用できるようになります。
スクリプトを V8 に移行する場合は、直接的な XML リテラルや、 オブジェクトです。
代わりに XmlService を使用して、 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 |
__iterator__
を使用してカスタム イテレータ関数をビルドしない
JavaScript 1.7 で、任意のクラスにカスタム イテレータを追加できる機能が追加されました
そのクラスのプロトタイプで __iterator__
関数を宣言します。これは
も、デベロッパーの便宜上、Apps Script の Rhino ランタイムに追加されています。ただし、この機能は ECMA-262 標準の一部ではなく、ECMAScript 準拠の JavaScript エンジンから削除されています。V8 を使用するスクリプト
このイテレータ構造は使用できません
スクリプトを V8 に移行する場合、ビルドに __iterator__
関数を使用しないようにします。
カスタム イテレータ。代わりに
ECMAScript 6 イテレータを使用します。
次の配列構成について考えてみましょう。
// 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. |
次のコードサンプルは、 Rhino ランタイムと、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); } |
条件付きの catch 句を避ける
V8 ランタイムでは、catch..if
条件付きキャッチ句は、次のとおり、サポートしていません。
標準に準拠していません
スクリプトを V8 に移行する場合は、catch 条件を 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()
を使用しない
JavaScript 1.3 には、 Object.prototype.toSource() 新しいメソッドです。V8 ランタイムではサポートされていません。
スクリプトを V8 に移行する場合は、 Object.prototype.toSource() コードから分離します
その他の相違点
スクリプトのエラーの原因となる上記の非互換性の他に、 相違点もあります。修正しないと、予期しない V8 が発生する可能性があります。 スクリプトの動作を制御します。
以降のセクションでは、このような問題を回避するためにスクリプト コードを更新する方法について説明します。 予想外の結果です
言語 / 地域固有の日付と時刻の形式を調整する
Date
メソッド toLocaleString()
、
toLocaleDateString()
,
と toLocaleTimeString()
の動作
V8 ランタイムは Rhino と異なります。
Rhino のデフォルトの形式は long 形式であり、 無視されます。
V8 ランタイムでは、デフォルトの形式は短い形式とパラメータです
ECMA 標準に従って処理されます(詳しくは、
toLocaleDateString()
のドキュメント
をご覧ください)。
スクリプトを V8 に移行する際は、言語 / 地域固有の日時メソッドの出力に関するコードの期待値をテストして調整します。
// 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
と Error.lineNumber
を使用しない
V8 の場合、標準の JavaScript
Error
オブジェクトがコンストラクタ パラメータとして fileName
または lineNumber
をサポートしていない
またはオブジェクトのプロパティを指定します。
スクリプトを V8 に移行する際
Error.fileName
と Error.lineNumber
への依存を排除する。
別の方法として、
Error.prototype.stack
。
このスタックも標準ではありませんが、Rhino と V8 の両方でサポートされています。「
2 つのプラットフォームによって生成されるスタック トレースの形式が若干異なります。
// 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) |
文字列化された列挙型オブジェクトの処理を調整
元の Rhino ランタイムでは、
JSON.stringify()
メソッドは {}
のみを返します。
V8 では、列挙型オブジェクトに対して同じメソッドを使用すると、列挙型名が返されます。
スクリプトを V8 に移行する際は、列挙型オブジェクトに対する JSON.stringify()
の出力に関するコードの期待値をテストして調整します。
// 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" |
未定義パラメータの処理を調整
元の Rhino ランタイムで、パラメータとしてメソッドに undefined
を渡す
このメソッドに文字列 "undefined"
が渡されました。
V8 では、undefined
をメソッドに渡すことは、null
を渡した場合と同じです。
スクリプトを V8 に移行する際
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. |
グローバル this
の処理を調整
Rhino ランタイムでは、それを使用するスクリプト用に特別なコンテキストが暗黙的に定義されています。
スクリプト コードは、実際のグローバル
this
。これは、「グローバル this
」への参照を意味します。実際のコードでは
特殊なコンテキストに評価されます。このコンテキストには、コードと変数のみが
定義します。組み込みの Apps Script サービスと ECMAScript オブジェクト
this
の使用から除外されます。似たような状況でした
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. }(); |
V8 では、暗黙的な特殊コンテキストが削除されます。グローバル変数と関数
グローバル コンテキストに配置され、組み込みの
Apps Script サービスと ECMAScript 組み込み(Math
、Date
など)
スクリプトを V8 に移行する場合は、コードをテストして調整する
グローバル コンテキストでの this
の使用について。ほとんどの場合、
状態変数が明らかになるのは、コードのキーまたはプロパティ名を
グローバル 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)); } |
ライブラリ内の instanceof
の処理を調整
パラメータとして渡されたオブジェクトのライブラリで instanceof
を使用する
偽陰性が発生する可能性があります。V8 ランタイムでは、プロジェクトとそのライブラリは異なる実行コンテキストで実行されるため、グローバルとプロトタイプ チェーンが異なります。
これは、ライブラリがオブジェクトで instanceof
を使用する場合にのみ該当します。
プロジェクトに作成されません。作成されたオブジェクトに対して使用すると、
使用するスクリプトがプロジェクト内で同じか別のかにかかわらず、
想定どおりに動作するはずです
V8 で実行されているプロジェクトでスクリプトをライブラリとして使用している場合は、
スクリプトでは、別のプロジェクトから渡されるパラメータに instanceof
を使用しています。調整
instanceof
の使用方法と、用途に応じて他の実行可能な代替手段を使用する
ケースです
a instanceof b
の代わりに、a
のコンストラクタを使用できます。
プロトタイプ チェーン全体を検索する必要がなく、
渡します。
使用方法: a.constructor.name == "b"
プロジェクト A とプロジェクト B で、プロジェクト A がプロジェクト B をライブラリとして使用するとします。
//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 } |
別の方法として、メイン プロジェクトで instanceof
をチェックする関数を導入することもできます。
ライブラリ関数を呼び出すときに、他のパラメータに加えて関数を渡します。渡される関数
その後、これを使用してライブラリ内の instanceof
をチェックできます。
//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); } |
非共有リソースのライブラリへの受け渡しを調整
非共有リソースをメイン スクリプトからライブラリに渡す方法は、V8 ランタイムでは異なります。
Rhino ランタイムでは、共有されていないリソースを渡しても機能しません。ライブラリは代わりに独自のリソースを使用します。
V8 ランタイムでは、非共有リソースをライブラリに渡しても機能します。ライブラリは、渡された非共有リソースを使用します。
共有されていないリソースを関数パラメータとして渡さないでください。非共有リソースは常に、それを使用する同じスクリプトで宣言します。
プロジェクト A とプロジェクト B で、プロジェクト A がプロジェクト B をライブラリとして使用するとします。この例では、PropertiesService
は非共有リソースです。
// 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')); } |
スタンドアロン スクリプトへのアクセス権を更新する
V8 ランタイムで動作するスタンドアロン スクリプトでは、少なくとも スクリプトのトリガーが正常に動作するように、スクリプトへの閲覧権限を許可する必要があります。