どうも。つじけ(tsujikenzo)です。このシリーズでは、2021年5月から始まりました「ノンプロ研GAS中級講座5期」について、全8回でお届けします。今日は3回目のDay2です。
前回のおさらい
前回は、「スコープと関数」 をお届けしました。スコープでは、主にグローバル領域で宣言された変数が、プロジェクト全体から呼び出せる仕様などを学びました。
また、関数では、関数化のメリットをはじめ、関数化することによって気を払わなければならない 「引数と仮引数のやりとり」 について学びました。
今日は、Day2で 「クラス・ライブラリ」 についてお届けします。
今日のアジェンダ
- クラス
- プロパティの定義
- メソッドの定義
- ライブラリ
クラス
学習のコツ
プログラミング学習のコツは 「まず、書き方を徹底して覚えること」 です。講座でもお伝えしたクラスの定義を覚えて、実際に書くことをオススメします。
class クラス名{
constructor(property){
this.property = property;
}
method(){
return this.property;
}
}
私は、この定義をまるまる辞書登録して、 「class;」 で呼び出せるようにしています。なので、コンストラクタのスペル(constructor)を忘れても怖くありません。笑
ただ、 「書いて覚える」 という作業は 「こういうものだから覚えてしまえ」 という少々乱暴なものです。中級以降は覚えることも増えていきます。「なぜこう書くのか」 を学習することも、長期記憶定着の一助になると思います。補講ではクラスについて、少し掘り下げてみましょう。
クラスに歴史あり
はじめて 「クラス」 を学ぶと、書籍やWEBページなどの情報が多くて、何が正しいのかわからなくなることが多いです。 「クラス」 は、時代やプログラミング言語によってできることやとらえ方が違います。
皆さんが学んでいる Google Apps Script は JavaScript をベースとしています。クラスの歴史から勉強しようということで、JavaやC++のクラスを学ぶものありですが、そのまま JavaScript に当てはめられるかというと少し難ありです。
なぜ「クラス」があるのか
プログラミング言語がこの世に登場したときは、 「クラス」 はまだありませんでした。プログラミングは、まず電気信号をパソコンが理解できる言語に変換しなければなりませんでした。それができたら、次は 「順次、反復、分岐」 の3つの制御構造を手続き単位で組み立てたら便利だよね、といった発展を重ねていきます。
そして、処理が増えるにつれて「共通する処理はまとめたい」、「共通する処理を独立させて管理を楽にできないのかな」といった声が出てきます。そこで登場したのが 「Simula(シミュラ)」 というプログラミング言語です。
「Simula(シミュラ)」 には 「カプセル化(隠蔽)」 、「ポリモーフィズム」 、 「継承」(この3つをオブジェクト指向3大要素と呼びます)の機能が搭載されました。これは、先ほどまでの悩みだった 「共通する処理はまとめたい」 、 「共通する処理を独立させて管理を楽にできないのかな」 を実現するアイディアでした。
オブジェクト指向の誕生
「Simula(シミュラ)」 をさらに発展させた 「Smalltalk(スモールトーク)」 では、オブジェクト指向3大要素を、数学の 集合論 に応用し、現実世界を「モノ」と「模型」で表現できるようになりました。これは世紀の大発明で、発明したアラン・ケイ氏は数学のノーベル賞である チューリング賞 を受賞しています。
クラスも完璧ではなかった
そんなクラス=オブジェクト指向の歴史ですが、現実世界をなんでも表現できるかというと少し無理がありました。Smalltalkを使っていると、 「クラスやオブジェクトは、将来どのような機能を持つのか確実に予測できないと、クラス階層を正しく設計することはできない。」 という問題が発生したのです。
たとえば、高速で走る「スポーツカー」を製造したいだけのに、元の鋳型に「建材を運ぶ」という機能があると、「スポーツカーに建材を運ぶ」という機能が備わってしまう問題です。
また、スポーツカーは「高速で走る」が目的ですが、このようにすべてのオブジェクトが将来どんな機能を持つのか予測しないと、 クラスが設計できない という問題です。
その問題を解決するアイディアが 「プロトタイプベース」 という、 「クラスからクローンを作る」 手法です。 「新しいオブジェクトを作るときはクラスのインスタンスを作る」 という、 「クラスベース」 と違った手法をとりました。
Smalltalkの研究者が作った 「Self」 というプログラミング言語の解説ページが、とても参考になりました。
JavaScriptはプロトタイプベース
JavaScriptは プロトタイプベース を基本設計に取り入れています。クラスからクローンを作る仕組みは、関数リテラルをベースにすると分かりやすいと思います。
//関数リテラルによるオブジェクトの生成
function myFunction() {
const Person = (name, age) => {
const p = {};
p.name = name;
p.age = age;
return p;
};
console.log(Person('Bob', 25)); //{ name: 'Bob', age: 25 }
console.log(Person('Tom', 32)); ////{ name: 'Tom', age: 32 }
}
//コンストラクト関数とNew演算子によるインスタンスの生成
//コンストラクト関数にアロー関数は使えないので注意
function myFunction_02() {
const Person = function (name, age) {
this.name = name;
this.age = age;
};
console.log(new Person('Bob', 25)); //{ name: 'Bob', age: 25 }
console.log(new Person('Tom', 32)); //{ name: 'Tom', age: 32 }
}
//ES6から導入されたクラス構文
function myFunction_03() {
class Person {
constructor (name, age) {
this.name = name;
this.age = age;
}
}
console.log(new Person('Bob', 25)); //{ name: 'Bob', age: 25 }
console.log(new Person('Tom', 32)); //{ name: 'Tom', age: 32 }
}
糖衣構文
ES6で導入された 「クラス構文」 は、 「クラスベース」 で使われている構文を、JavaScriptでも書けるようになったもので、JavaScriptがクラスベースになったわけではありません。このように、読み書きのしやすさのために導入される書き方を 「糖衣構文(シンタクティックシュガー)」 と呼びます。
配列を生成する new Array を [] と書けるのも、糖衣構文です。
const array = new Array;
↓こう書ける
const array = [];
プロトタイプチェーン
プロトタイプベースでは、オブジェクトを作ると、裏側で prototype というプロパティを作成します。
クラスで作成されたインスタンスのメソッドは、インスタンスに置かず、prototypeプロパティを参照する仕組みになっています。これを 「プロトタイプチェーン」 と呼びます。今後も講座の中で紹介する場面があるかもしれません。
クラスの使いどころ
以前は、ノンプロ研内でも「GASではあまりクラスを書かないんじゃないか」という空気がありました。クラスは組み込みオブジェクトや、GWSで用意されているもので、我々はメンバーを呼び出すだけでよかったからです。
我々ノンプログラマーは、大規模な開発をすることがないので、あまり縁がありませんでしたが、V8や新IDEの登場で、クラス構文もスッキリかけるようになりました。 「クラス化で開発・保守の手助けをしてくれそう」 という声もあがってきています。
たとえば、日付に関する処理 をクラス化する方法があります。これまではこのように関数化してきたと思います。
//実行用関数
function testfunction() {
const d = new Date('2021/06/20');
console.log(getLastMonthOfDate_(d));
console.log(isWeekEnd_(d));
}
//与えられたDateオブジェクトの前月の月末の日付を整数で返すサブ関数
function getLastMonthOfDate_(date) {
const d = new Date(date);
d.setDate(0);
return d.getDate();
}
//与えられたDateオブジェクトが土日かどうか返すサブ関数
function isWeekEnd_(date) {
const d = new Date(date);
return d.getDay() === 6 || d.getDay() === 0;
}
その、サブ関数をクラス化したものがこちらです。クラスをグローバル領域に書くことで、プロジェクト全体からアクセスできます。
//クラス化したもの
class MyDate {
constructor(date) {
this.d = date;
}
getLastMonthOfDate() {
this.d.setDate(0)
return this.d.getDate();
}
isWeekEnd() {
return this.d.getDay() === 5 || this.d.getDay() === 6;
}
}
//実行用関数
function myFunction2_98() {
const d = new Date('2021/06/20');
const d1 = new MyDate(d);
console.log(d1.getLastMonthOfDate());
console.log(d1.isWeekEnd());
}
グローバル領域に書くと、プロジェクト全体からアクセスできるけど、書き換えられてしまうこともあるのでは?と思ったかたもいるかもしれません。
そうです。グローバル領域に書かれた変数や関数は、いとも簡単に書き換えられてしまうのです。
let Person = function (name) {
this.name = name;
}
const p = new Person('大山');
console.log(`私は${p.name}です`); //私は大山です
function myFunction_04() {
Person = function (name) {
this.name = name + '様';
}
const rp = new Person('大山');
console.log(`私は${rp.name}です`); //私は大山様です
}
クラスはこのようなグローバル変数のスコープを閉じ込める役割ももっています。
ライブラリ
最後のパートはライブラリについてでした。
HEAD(開発モード)
ライブラリを操作していると、これを一度は目にしたことがあると思います。
ライブラリを追加する際、ユーザーはライブラリの 「バージョン」 を選択します。もしあなたが ライブラリの開発者アカウントでログイン しているなら、 「HEAD(開発モード)」 というバージョンを設定できます、という意味です。
これは、ライブラリを公開したとき、共有権限を「閲覧者」ではなく「編集者」にした場合も、全てのユーザーに「HEAD(開発モード)」が表示されます。気を付けましょう。
「HEAD(開発モード)」 は、ライブラリが更新されたら、即時に更新を反映させます。
ライブラリが更新されて、バグが修正されたり、機能が追加されたりした場合は、ユーザーがバージョンをアップデートする必要があります。
「そんなのめんどくさいよ。勝手にアップデートしてよ」と思うかもしれませんが、セキュリティ的に考えると、一方的にアップデートされるのは危険でしょう。「パソコンが壊れろ」というコードを書いたバージョンが更新されて実行されるリスクがあるからです。
ライブラリの詳細
スクリプトエディタには、ライブラリ詳細を確認する機能があります。
ライブラリメニューにある [縦3点メニュー] をクリックして、 [新しいタブで開く] をクリックします。
別タブが開いて、ライブラリの詳細を確認できます。
これはライブラリに書かれてある関数を自動的に拾ってくれる機能ですが、ドキュメンテーションコメント を書くと、より詳細を反映してくれます。日頃から 「関数を作成したらドキュメンテーションコメントを書く」 というルールを課すようにしましょう。
なお、この機能は、ライブラリのバージョンを 「HEAD(開発モード)」 以外に設定する必要があります。下記のようなメッセージが表示されたら、ライブラリのバージョンが 「HEAD(開発モード)」 になっていない確認しましょう。
まとめ
以上で、 「クラス・ライブラリ」 をお届けしました。少しボリュームが出ましたが、クラスは中級講座の最大の山場ですので、スペースをさいて説明させていただきました。
講師も改めて学習しなおし、アウトプットさせていただいています。感謝申し上げます。
さて、本文でも触れましたが、大規模な開発をしなければ、クラスは不要かもしれません。しかし例にあげたような 「日付に関する処理をまとめる」 や 「シートに関する処理をまとめる」 といったクラス化は、プロジェクト全体のメンテナンス性を向上させますし、なにより リーダブル です。
小さなコードからでも構いませんが、ぜひリファクタリングする際などは、クラス化にチャレンジしてみましょう。
次回は、 「組み込みオブジェクト」 をお届けします。
このシリーズの目次
- [ノンプロ研]GAS中級講座5期 事前課題
- [ノンプロ研]GAS中級講座5期Day1 スコープと関数
- [ノンプロ研]GAS中級講座5期Day2 クラス・ライブラリ
- [ノンプロ研]GAS中級講座5期Day3 組み込みオブジェクト
- [ノンプロ研]GAS中級講座5期Day4 ScriptServices1
- [ノンプロ研]GAS中級講座5期Day5 ScriptServices2
- [ノンプロ研]GAS中級講座5期Day6 HTTP通信・API
- [ノンプロ研]GAS中級講座5期卒業LT ノンプログラマーによるGAS開発モデルとは
- [ノンプロ研]GAS中級講座5期卒業LT プロの開発を知ろう
- [ノンプロ研]GAS中級講座5期卒業LT ノンプログラマーによる開発