V8 執行階段總覽

在 Google Apps Script 和 JavaScript 中,執行階段執行階段環境包含 JavaScript 引擎,可剖析及執行指令碼程式碼。執行階段會提供記憶體存取方式、程式與電腦作業系統互動方式,以及合法程式語法的規則。每個網路瀏覽器都有 JavaScript 的執行階段環境。

過去,Apps Script 一直採用 Mozilla 的 Rhino JavaScript 解譯器。Rhino 雖然能讓 Apps Script 輕鬆執行開發人員指令碼,但也將 Apps Script 綁定至特定 JavaScript 版本 (ES5)。使用 Rhino 執行階段的指令碼,Apps Script 開發人員無法使用更現代的 JavaScript 語法和功能。

為解決這項疑慮,Apps Script 現在支援 Chrome 和 Node.js 採用的 V8 執行階段。將現有指令碼遷移至 V8,即可使用新版 JavaScript 語法和功能。

本頁面說明 V8 啟用的新功能,以及如何啟用 V8,以便在指令碼中使用。「將指令碼遷移至 V8」一文說明如何遷移現有指令碼,改用 V8 執行階段。

V8 執行階段的功能

使用 V8 執行階段的指令碼可運用下列功能:

新式 ECMAScript 語法

在 V8 執行階段支援的指令碼中,使用新版 ECMAScript 語法。這項語法包括 letconst 和許多其他熱門功能。

如要查看可使用 V8 執行階段進行的熱門語法改良項目簡短清單,請參閱「V8 語法範例」。

與其他常見的 JavaScript 執行階段相比,Apps Script V8 執行階段有一些限制和主要差異。詳情請參閱「Apps Script V8 執行階段限制」。

改善功能偵測

使用 V8 的指令碼可更準確地偵測 Apps Script 函式。 新版執行階段可辨識下列函式定義格式:

      function normalFunction() {}
      async function asyncFunction() {}
      function* generatorFunction() {}

      var varFunction = function() {}
      let letFunction = function() {}
      const constFunction = function() {}

      var namedVarFunction = function alternateNameVarFunction() {}
      let namedLetFunction = function alternateNameLetFunction() {}
      const namedConstFunction = function alternateNameConstFunction() {}

      var varAsyncFunction = async function() {}
      let letAsyncFunction = async function() {}
      const constAsyncFunction = async function() {}

      var namedVarAsyncFunction = async function alternateNameVarAsyncFunction() {}
      let namedLetAsyncFunction = async function alternateNameLetAsyncFunction() {}
      const namedConstAsyncFunction = async function alternateNameConstAsyncFunction() {}

      var varGeneratorFunction = function*() {}
      let letGeneratorFunction = function*() {}
      const constGeneratorFunction = function*() {}

      var namedVarGeneratorFunction = function* alternateNameVarGeneratorFunction() {}
      let namedLetGeneratorFunction = function* alternateNameLetGeneratorFunction() {}
      const namedConstGeneratorFunction = function* alternateNameConstGeneratorFunction() {}

      var varLambda = () => {}
      let letLambda = () => {}
      const constLambda = () => {}

      var varAsyncLambda = async () => {}
      let letAsyncLambda = async () => {}
      const constAsyncLambda = async () => {}

從觸發條件和回呼呼叫物件方法

使用 V8 的指令碼可以從您已可呼叫程式庫方法的位置,呼叫物件方法和類別靜態方法。包括:

以下 V8 範例說明在 Google 試算表中建構選單項目時,如何使用物件方法:

function onOpen() {
  const ui = SpreadsheetApp.getUi(); // Or DocumentApp, SlidesApp, or FormApp.
  ui.createMenu('Custom Menu')
      .addItem('First item', 'menu.item1')
      .addSeparator()
      .addSubMenu(ui.createMenu('Sub-menu')
          .addItem('Second item', 'menu.item2'))
      .addToUi();
}

const menu = {
  item1: function() {
    SpreadsheetApp.getUi().alert('You clicked: First item');
  },
  item2: function() {
    SpreadsheetApp.getUi().alert('You clicked: Second item');
  }
}

查看記錄

Apps Script 提供兩種記錄服務:Logger 服務console 類別。這兩項服務都會將記錄寫入相同的 Stackdriver Logging 服務

如要顯示 Loggerconsole 記錄,請按一下腳本編輯器頂端的「執行記錄」

查看執行作業

如要查看指令碼的執行記錄,請開啟 Apps Script 專案,然後按一下左側的「執行」

「執行」面板不會提供個別 Apps Script 服務呼叫的時間戳記記錄。使用 console 服務建立適當的記錄訊息。使用 console 建立的所有記錄都會顯示在「Executions」(執行) 面板中。

V8 語法範例

以下簡要列出 V8 執行階段指令碼可用的熱門語法功能。

letconst

letconst 關鍵字分別可讓您定義區塊範圍本機變數和區塊範圍常數。

// V8 runtime
let s = "hello";
if (s === "hello") {
  s = "world";
  console.log(s);  // Prints "world"
}
console.log(s);  // Prints "hello"

const N = 100;
N = 5; // Results in TypeError
      

箭頭函式

箭頭函式 提供簡潔的方式,可在運算式中定義函式。

// Rhino runtime
function square(x) {
  return x * x;
}

console.log(square(5));  // Outputs 25
      
// V8 runtime
const square = x => x * x;
console.log(square(5));  // Outputs 25

// Outputs [1, 4, 9]
console.log([1, 2, 3].map(x => x * x));
      

類別

類別 提供一種方法,可透過繼承從概念上整理程式碼。V8 中的類別主要是在 JavaScript 原型式繼承上進行語法糖化。

// V8 runtime
class Rectangle {
  constructor(width, height) { // class constructor
    this.width = width;
    this.height = height;
  }

  logToConsole() { // class method
    console.log(`Rectangle(width=${this.width}, height=${this.height})`);
  }
}

const r = new Rectangle(10, 20);
r.logToConsole();  // Outputs Rectangle(width=10, height=20)
      

解構指派項目

解構指派運算式可快速將陣列和物件中的值解壓縮至不同的變數。

// Rhino runtime
var data = {a: 12, b: false, c: 'blue'};
var a = data.a;
var c = data.c;
console.log(a, c);  // Outputs 12 "blue"

var a = [1, 2, 3];
var x = a[0];
var y = a[1];
var z = a[2];
console.log(x, y, z);  // Outputs 1 2 3
      
// V8 runtime
const data = {a: 12, b: false, c: 'blue'};
const {a, c} = data;
console.log(a, c);  // Outputs 12 "blue"


const array = [1, 2, 3];
const [x, y, z] = array;
console.log(x, y, z);  // Outputs 1 2 3


      

樣板字面值

範本常值 是允許內嵌運算式的字串常值。可避免更複雜的字串串連陳述式。

// Rhino runtime
var name =
  'Hi ' + first + ' ' + last + '.';
var url =
  'http://localhost:3000/api/messages/'
  + id;
      
// V8 runtime
const name = `Hi ${first} ${last}.`;
const url =
  `http://localhost:3000/api/messages/${id}`;


      

預設參數

預設參數可讓您在函式宣告中,為函式參數指定預設值。這樣一來,您就不必明確為遺漏的參數指派預設值,因此可以簡化函式主體中的程式碼。

// Rhino runtime
function hello(greeting, name) {
    greeting = greeting || "hello";
    name = name || "world";
    console.log(
        greeting + " " + name + "!");
}

hello();  // Outputs "hello world!"
      
// V8 runtime
const hello =
  function(greeting="hello", name="world") {
      console.log(
        greeting + " " + name + "!");
  }

hello();  // Outputs "hello world!"

      

多行字串

使用與範本字面值相同的語法,定義多行字串。與樣板字面值相同,這個語法可避免字串串連,並簡化字串定義。

// Rhino runtime
var multiline = "This string is sort of\n"
+ "like a multi-line string,\n"
+ "but it's not really one.";
      
// V8 runtime
const multiline = `This on the other hand,
actually is a multi-line string,
thanks to JavaScript ES6`;
      

V8 執行階段限制

Apps Script V8 執行階段並非標準 Node.js 或瀏覽器環境。如果您呼叫第三方程式庫,或改編其他 JavaScript 環境的程式碼範例,可能會導致相容性問題。

無法使用的 API

下列標準 JavaScript API「無法」在 Apps Script V8 執行階段中使用:

  • 計時器setTimeoutsetIntervalclearTimeoutclearInterval
  • 串流ReadableStreamWritableStreamTextEncoderTextDecoder
  • 網路 APIfetchFormDataFileBlobURLURLSearchParamsDOMExceptionatobbtoa
  • 加密cryptoSubtleCrypto
  • 全域物件windownavigatorperformanceprocess (Node.js)

請改用下列 Apps Script API:

如果 API 沒有 Apps Script 替代方案 (例如 TextEncoder),有時可以使用 Polyfill。Polyfill 是一種程式庫,可複製執行階段環境中預設未提供的 API 功能。使用 Polyfill 前,請確認該項目與 Apps Script 的 V8 執行階段相容。

非同步限制

V8 執行階段支援 asyncawait 語法,以及 Promise 物件。不過,Apps Script 執行階段環境基本上是同步的。

  • 微工作 (支援):執行階段會在目前的呼叫堆疊清除後,處理微工作佇列 (其中會發生 Promise.then 回呼和 await 解析)。
  • 巨集工作 (不支援):Apps Script 沒有巨集工作的標準事件迴圈。無法使用 setTimeoutsetInterval 等函式。
  • WebAssembly 例外狀況:WebAssembly API 是唯一可在執行階段以非封鎖方式運作的內建功能,可支援特定非同步編譯模式 (WebAssembly.instantiate)。

所有 I/O 作業 (例如 UrlFetchApp.fetch) 都會造成阻斷。如要達成平行網路要求,請使用 UrlFetchApp.fetchAll

課程限制

V8 執行階段對新式 ES6 以上類別功能有特定限制:

  • 私有欄位:系統不支援私有類別欄位 (例如 #field),因此會導致剖析錯誤。如要進行真正的封裝,請考慮使用閉包或 WeakMap
  • 靜態欄位:不支援類別主體內的直接靜態欄位宣告 (例如 static count = 0;)。在類別定義後,將靜態屬性指派給類別 (例如 MyClass.count = 0;)。

模組限制

  • ES6 模組:V8 執行階段不支援 ES6 模組 (import/export)。如要使用程式庫,您必須使用 Apps Script 程式庫機制,或將程式碼及其依附元件組合為單一指令碼檔案。(問題追蹤器)
  • 檔案執行順序:專案中的所有指令碼檔案都會在全域範圍內執行。建議您避免使用具有副作用的頂層程式碼,並確保函式和類別是在跨檔案使用前定義。如果檔案之間存在依附元件,請在編輯器中明確排序檔案。

啟用 V8 執行階段

如果指令碼使用 Rhino 執行階段,請按照下列步驟切換至 V8:

  1. 開啟 Apps Script 專案。
  2. 按一下左側的「專案設定」
  3. 勾選「啟用 Chrome V8 執行階段」核取方塊。

或者,您也可以編輯指令碼資訊清單檔案,直接指定指令碼執行階段:

  1. 開啟 Apps Script 專案。
  2. 按一下左側的「專案設定」
  3. 勾選「在編輯器中顯示『appsscript.json』資訊清單檔案」核取方塊。
  4. 按一下左側的「編輯器」圖示 > appsscript.json
  5. appsscript.json 資訊清單檔案中,將 runtimeVersion 欄位設為 V8 值。
  6. 按一下頂端的「儲存專案」圖示

將指令碼遷移至 V8 一文說明瞭其他步驟,可確保指令碼在 V8 中正常運作。

啟用 Rhino 執行階段

如果您的指令碼使用 V8,但需要改用原始的 Rhino 執行階段,請按照下列步驟操作:

  1. 開啟 Apps Script 專案。
  2. 按一下左側的「專案設定」圖示
  3. 取消勾選「啟用 Chrome V8 執行階段」核取方塊。

或者,也可以編輯指令碼資訊清單:

  1. 開啟 Apps Script 專案。
  2. 按一下左側的「專案設定」圖示
  3. 勾選「在編輯器中顯示『appsscript.json』資訊清單檔案」核取方塊。
  4. 按一下左側的「編輯器」圖示 > appsscript.json
  5. appsscript.json 資訊清單檔案中,將 runtimeVersion 欄位設為 DEPRECATED_ES5 值。
  6. 按一下頂端的「儲存專案」圖示

如何遷移現有指令碼?

請參閱「將指令碼遷移至 V8」指南,瞭解如何將現有指令碼遷移至 V8。包括啟用 V8 執行階段,以及檢查指令碼是否有任何已知的不相容問題。

自動將指令碼遷移至 V8

自 2020 年 2 月 18 日起,Google 會逐步將通過自動相容性測試的現有指令碼遷移至 V8。遷移後,受影響的指令碼仍可正常運作。

如要讓指令碼不進行自動遷移,請將資訊清單中的 runtimeVersion 欄位設為 DEPRECATED_ES5。之後隨時可以選擇手動將指令碼遷移至 V8

如何回報錯誤?

支援指南說明如何透過 Stack Overflow 取得程式設計協助、搜尋現有問題報告、回報新錯誤,以及要求新功能。