みなさん、こんにちは!
タカハシ(@ntakahashi0505)です。
「初心者向けGoogle Apps Scriptでクラスを作る!」シリーズを連載でお伝えしています。
前回の記事はこちらです。
クラスにプロパティを定義するdefinePropertiesメソッドの使い方についてお伝えしました。
今回は、ちょっと方向を変えていきますね…
即時関数ですよ、即時関数。
Google Apps Scriptで即時関数にクラスを定義する理由とその方法についてお伝えします。
では、行ってみましょう!
前回のおさらい
まず、以下のようなスプレッドシートの表があります。
この表の各行を表すクラスを作成しているのですが、前回まで作成したのがこちらです。
function myFunction() { var Person = function(record){ var _id = record[0]; this.name = record[1]; this.gender = record[2]; this.birthday = record[3]; Object.defineProperties(this, { id: { get: function(){ return _id; } } }); }; var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); var p = new Person(values[1]); Logger.log(p.id); p.id = 'a00'; Logger.log(p.id); }
ひとまずクラスとして形になってきましたね。
ただ、クラスPersonはmyFunctionの中で定義しているので、myFunctionの中でしか使用できないですよね。
他の関数からもクラスを利用したいときはどうすれば良いでしょうか?
クラスをグローバルに定義する
クラスPersonの定義、つまりコンストラクタをグローバルに定義しちゃいましょう。
var Person = function(record){ var _id = record[0]; this.name = record[1]; this.gender = record[2]; this.birthday = record[3]; Object.defineProperties(this, { id: { get: function(){ return _id; } } }); }; Person.prototype.greet = function(){ Browser.msgBox(this.name + "です、こんにちは!"); }; Person.prototype.log = function(){ Logger.log('%s|%s|%s|%s|', this.id, this.name, this.gender, this.birthday); };
はい、OKです。
これでmyFunctionがスッキリです。
function myFunction() { var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues(); var p = new Person(values[1]); p.log(); }
はい、以上お疲れ様でした!
…だと、この記事ちょっと密度が薄すぎますね。
即時関数でスコープを作る
今回の場合、グローバルに定義したとしても、まあそんなに大きな問題はないのですが、定義する関数や変数が全部グローバルスコープになってしまうのが、ちょっと心配ですよね。
うっかり、同じ関数や変数があったら、わけがわからない不具合が出ちゃいそうです。
それを防ぐために、 即時変数でスコープを作るという手があります。
即時関数とは
即時関数というのは、一回だけすぐに実行される関数です。
書式は以下の通り。
//処理
})(引数1, 引数2,…)
名前がなくて、かっこで囲まれている感じですよね。
GASの話ですが、プロジェクト内のいずれかの関数が実行されたとき、グローバルに定義されているステートメントはその時点ですべて実行されます。
ですから、その辺のグローバルにポロっと置いてあるステートメントを、即時関数の中に入れ込んであげるんです。
すると、即時関数も関数ですから、その内部の変数や処理がローカルスコープに閉じ込めることができるというわけです。
すごいぞ!即時関数!
クラスを即時関数内に定義する
では、今回のクラスPersonをグローバル領域の即時関数内に定義してみましょう。
こちらです。
(function(global){ var Person = function(record){ var _id = record[0]; this.name = record[1]; this.gender = record[2]; this.birthday = record[3]; Object.defineProperties(this, { id: { get: function(){ return _id; } } }); }; Person.prototype.greet = function(){ Browser.msgBox(this.name + "です、こんにちは!"); }; Person.prototype.log = function(){ Logger.log('%s|%s|%s|%s|', this.id, this.name, this.gender, this.birthday); }; global.Person = Person; })(this);
先ほどのmyFunctionを実行すると、ちゃんと以下のようにログが出力されるはずです。
thisとglobalの意味
さて、この即時関数ですが、引数としてthisを渡して、それをglobalで受け取ってますね。
そしてさらに26行目ですが、globalのPersonにPersonを渡すという謎の処理が見受けられます。
これについて、ちゃんと理解しておく必要があります。
まず、thisですが、コンストラクタ内で使われているthisとは全く別の意味があります。
ここで使われているthisはグローバルのthisなのですが、その場合のthisはグローバルオブジェクトを表します。
それを、即時関数の仮引数のglobalに渡していますが、これはただの即時関数内で使用する仮引数です。
別にhogeでも良いわけです。
そして26行目。
global.Person = Person
これは、即時関数内で宣言したPersonをグローバルオブジェクトにも定義しているということですね。
ほら、コンストラクタPersonは即時関数内にありますからそのままではローカルスコープ。つまり、他の関数からは使用することができません。
それを、グローバルオブジェクトに渡すことで、プロジェクト内のすべての関数から呼び出すことができるようになるわけです。
まとめ
以上、Google Apps Scriptで即時関数にクラスを定義する理由とその方法についてお伝えしました。
GAS(というかJavaScript)はスコープがすごく重要ですよね。
あと、this。使う場所によって役割が変わりますので、要注意ですね。
次回は、インスタンスの集合を取り扱いしていきます。
どうぞお楽しみに!