あまり知られていない Google Apps Script の暗黙オブジェクトについて、調べたのでまとめておく。
GAS の「サーブレット」と暗黙オブジェクト
GAS はサーバーレスで気軽に Web アプリケーションを作れることでお馴染みだが、Html テンプレートの仕様についての情報が PHP や Java に比べて乏しい。
HtmlTemplate.getCode()
で Java でいうサーブレットのようなものを取得できるので、Logger なりなんなりで見てみると、冒頭に以下のような宣言が行われていることに気づく。
(function() {
var output = HtmlService.initTemplate();
. . .
これこそが GAS における唯一の暗黙オブジェクト output
の宣言に他ならない。
暗黙オブジェクト output の詳細
GAS の入門書には(何冊か眺めた限りでは)暗黙オブジェクトについては記載がない(もっとも、GAS には暗黙オブジェクトは output
しかないので、別にサーブレットのようなものを入手しても何の面白みもないし、格別にコードを書くのが快適になったりすることはない)。output
は次のように、明示的に代入を行うのに使うことができる。
専用スクリプトレット | 別の書き方 | |
---|---|---|
PHP | <?=$hoge?> |
<?php echo $hoge; ?> |
JAVA | <%= hoge %> |
<% out.println(hoge) %> |
GAS | <?=$hoge?> |
<? output._ = hoge ?> |
よく見ると、公式サイトにちょびっと解説があった。ざっくりと概要を追っていくと、
output
はHtmlService.initTemplate()
というメソッドにより生成される暗黙オブジェクトである。基本的に template が利用するものなのでドキュメントには記載していない。
この initTemplate()
というメソッドの詳細自体、公式ドキュメントに記載されていないし、GAS のスクリプトエディタでも補完対象になっていないが、明示的に打ち込んでもきちんと動く(サンプルコードは後述する)。
これは 特殊な
HtmlOutput
型オブジェクトで、append()
とappendUntrusted()
に対応する略記法としてプロパティ_
と_$
を持っている。output
はさらに$out
というプロパティを持っていて、これによりふつうのHtmlOutput
型オブジェクトに変換される。
略記法とは言うが、output.append(sth)
に対して略記法は output._ = sth
だから、構文の違いには注意する必要がある。
また、この特殊な HtmlOutput
型オブジェクトは、単に略記法が増えただけの HtmlOutput
型オブジェクトなのではない。そのまま doGet() から return してもエラーが出てブラウザで表示されない。もしこの特殊なオブジェクトをプログラマが明示的に扱いたい場合は、最終的に $out
を呼んで普通の HtmlOutput
型に変換する必要がある。V8 ランタイムではさらに HtmlOutput
型との差異が大きくなり、もはや全く別物と考えてよい。型の性質については、この記事の下部で見る。
evaluate
の過程で、スクリプトレット外部の HTML はoutput._ = sth
という形に変換され、スクリプトレット内部はそのまま Javascript コードとして扱われる。行番号は保存されるので、エラーが出たときに原因箇所を特定するのは楽。
これはまあ、JSP がサーブレットに変換される過程と同じか。
基本的な記法
たとえば1番から100 番まで列挙するアホスクリプトを書くことを考える。以下のように代入スクリプトレットを駆使して書く手法がよく挙げられる。
<div>
<? for (var i=1;i<100;i++) { ?>
<?= i.toString() + "番" ?>
<? } ?>
</div>
これは下のように暗黙オブジェクトを用いて書いてもよい。個人的にはちびちびと <? ?>
と <?= ?>
を切り替えて打つのは面倒くさくて嫌なので、暗黙オブジェクトの存在を知れたのはありがたい。
<div>
<?
for (var i=1;i<100;i++) {
output._ = i.toString() + "番";
}
?>
</div>
スクリプトレットを使わず、GAS ですべて完結させるなら以下のような書き方になる。明示的に作成する場合は変数名を自由に付けられる。最終的に .$out
を呼んで1普通の HtmlOutput
型に戻さないとエラーが出るので注意。
function doGet(){
var op = HtmlService.initTemplate(); // 特殊 HtmlOutput 型をつくる
op._ = "<html><body><div>";
for (var i=1;i<100;i++) {
op._ = i.toString() + "番";
}
op._ = "</div></body></html>";
return op.$out; // ここで $out を呼んでふつうの HtmlOutput 型に戻す
}
参考:Google Apps Scriptプログラミング [中級編] - Webアプリケーション開発とHtml Service (6/7)
型の相互関係とランタイムバージョン
HtmlService には多くのオブジェクト型が存在するが、この「特殊な HtmlOutput
型」(以降とりあえず HtmlOutputBeta
とする)と関連する型はほかの型とどういう関係になっているのだろうか。公式ドキュメントには "output is a special HtmlOutput object" とあるから、基本的に HtmlOutput
型の性質を継承しているものと思われるが、実はランタイムバージョンによって性質は大きく異なる。
まず RuntimeVersion
を Rhino にした状態では、以下のようになっており、基本的に HtmlOutputBeta
型は HtmlOutput
とほとんど同じ機能を有していることがわかる。
注意点としては、HtmlOutputBeta
型のままでは return しても表示できないこと、HtmlOutputBeta
型に .clear()
メソッドをあてると普通の HtmlOutput
型になること、HtmlOutputBeta
型は新規作成するほかなく、別のデータから変換する手段はないということだろうか。
ところが、この output
オブジェクトは、先日 GAS に導入された V8 ランタイムでは性質が変更されているようで、teratail にも質問が投稿されている。たとえば、略記法の output._ = sth;
は問題なく機能するが、略記元であるはずの output.append(sth)
を使おうとすると、output.append is not a function.
というエラーが表示されてしまう。それどころか、Rhino
版では使えた .setContent()
、.clear()
などといった HtmlOutput
型に存在するメソッドが軒並み使えなくなっているようだ。
V8 ランタイムが導入されて間もないが、この点は公式の V8 移行マニュアルにも記載されていないので、もし暗黙オブジェクトを使いこなして技巧的な Html を作成している方がいるならば、以降に際してあらためてコードをチェックする必要がある。
まとめ
- GAS には暗黙オブジェクト
output
が存在するが、output._ = sth
という.append(sth)
の略記法以外はたいして活用法がない。 - V8 ランタイムでは
output
の機能が大幅に制限されているので、これまで暗黙オブジェクトoutput
を利用して技巧的なコードを書いていた人は注意すべし。
-
絶対に
.$out
じゃないとダメかというとそうではなくて、後述するように Rhino ランタイムなら.asTemplate().evaluate()
とかでも普通のHtmlOutput
型に変換すること自体は可能です。 ↩