スクリプトの V8 ランタイムへの移行

Rhino ランタイムを使用している既存のスクリプトで V8 構文と機能を使用したい場合は、スクリプトを V8 に移行する必要があります。

Rhino ランタイムを使用して記述されたスクリプトのほとんどは、V8 ランタイムを使用して動作できます 調整を加えずに測定できます多くの場合、V8 構文を追加するための前提条件は、 スクリプトに備わっている V8 ランタイムを有効にする

ただし、V8 ランタイムを有効にした後にスクリプトが失敗したり、予期しない動作をしたりする可能性がある、非互換性その他の違いがいくつかあります。移行時 スクリプトを使用するには、スクリプト プロジェクトでこれらの問題を検索し、 是正してください。

V8 の移行手順

スクリプトを V8 に移行する手順は次のとおりです。

  1. V8 ランタイムを有効にする 使用します。
  2. 以下の互換性のない機能をよく確認してください。スクリプトを調べて、 互換性の問題があります。互換性の問題がある場合は スクリプト コードを調整して、問題を解消または回避できるようにしてください。
  3. 以下のその他の違いをよく確認してください。スクリプトを調べて、リストされている相違点の中に影響があるかどうかを確認します。 判断できます。スクリプトを調整して動作を修正します。
  4. 検出された非互換性やその他の違いを修正したら、必要に応じて V8 構文やその他の機能を使用するようにコードを更新できます。
  5. コードの調整が完了したら、スクリプトを入念にテストして、 確認します。
  6. スクリプトがウェブアプリや公開されているアドオンの場合、 必要があります 新しいバージョンを作成する 調整を加えた値が返されます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 に移行する場合は、変数や関数に名前を付けないようにする いずれかを使用 予約済みキーワード。 キーワード名が使用されないように、変数または関数の名前を変更します。一般的な使用例 名前としてのキーワードの割合は、classimportexport です。

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.fileNameError.lineNumber を使用しない

V8 の場合、標準の JavaScript Error オブジェクトがコンストラクタ パラメータとして fileName または lineNumber をサポートしていない またはオブジェクトのプロパティを指定します。

スクリプトを V8 に移行する際 Error.fileNameError.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 組み込み(MathDate など)

スクリプトを 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'));
}

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

スタンドアロン スクリプトへのアクセス権を更新する

V8 ランタイムで動作するスタンドアロン スクリプトでは、少なくとも スクリプトのトリガーが正常に動作するように、スクリプトへの閲覧権限を許可する必要があります。