どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。
前回のシリーズでは、第5章の考察をお届けしました。
今回から第6章をお送りします。第7章は取り扱わないため、輪読会自体は最終回となります。
目次と日程
第6章は「ライブラリとAPI設計」です。プログラミング学習を続けていけば、かならず必要性の高い共通処理をライブラリ化したり、APIとしてウェブアプリケーションの公開をするかもしれません。
過去一番大変な章だと思いますが、LT大会は2021年12月21日です。がんばりましょう。
- 第1章 JavaScriptに慣れ親しむ
- 第2章 変数のスコープ
- 第3章 関数の扱い
- 第4章 オブジェクトとプロトタイプ
- 第5章 配列とディクショナリ
- 第6章 ライブラリとAPI設計
- 第7章 並行処理
今日はさっそく1回目で、 「一貫した規約を維持しよう」 と 「メソッドチェーンをサポートしよう」 を2本お届けします。
テキスト第6章「ライブラリとAPI設計」の項目53、項目60に対応しています。
今日のアジェンダ
- 一貫した規約を維持しよう
- メソッドチェーンをサポートしよう
一貫した規約を維持しよう
項目53は、読み物として、ライブラリを作成するときの心構えが紹介されていました。
ライブラリを公開するなら、ライブラリ内の規約は一貫したものにしよう。ということです。
たとえば、一貫性のひとつにあげられるものが、 「引数は標準的な書き方に合わせよう」 です。
HTMLやDOMオブジェクトにおける、elementのサイズを指定するメソッドの引数は、「幅、高さ」の順です。
//ブログのアイキャッチ画像の適正サイズ
const widget = new Widget(1200,630);
たしかに、Google Workspaceのメンバーも、引数の順番は、縦(行)、横(列)という規則にしたがっています。
//Rangeオブジェクトを取得するメソッド
Sheet.getRange(row, column)
//Drawingオブジェクトの位置を設定するメソッド
Drawing.setPosition(anchorRowPos, anchorColPos, offsetX, offsetY)
このような、暗黙のルールがあるなら、「あなたの自作ライブラリも同様である」 という一貫性をもちましょう。
ユーザーが、あなたのライブラリの仕様書を読む時間を、1秒でも短くすることが大切です。
シグニチャ
プログラミングでは、関数やメソッドの名前、引数の数やデータ型、返り値の型などの組み合わせのことをシグネチャといいます。
シグニチャは一貫した規約を使いましょう、というお話でした。
メソッドチェーンをサポートしよう
Stringオブジェクトのreplace()メソッドの戻り値は、stringです。
なので、このように、戻り値に対してメソッドをつなげて記述できます。
この書き方を、「メソッドチェーン」 と呼びます。
function myFunction8_60_01() {
function escapeBasicHTML(str) {
return str.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const body = '"Hello World"テスト中です。<html></html>';
const text = escapeBasicHTML(body);
console.log(text); // => "Hello World"テスト中です。<html></html>
}
メソッドチェーンでかくと、このように、途中経過の値を代入する変数を用意する必要がありません。
function myFunction8_60_02() {
function escapeBasicHTML(str) {
const text1 = str.replace(/&/g, '&');
const text2 = text1.replace(/</g, '<');
const text3 = text2.replace(/>/g, '>');
const text4 = text3.replace(/"/g, '"');
const text5 = text4.replace(/'/g, ''');
return text5;
}
const body = '"Hello World"テスト中です。<html></html>';
const text = escapeBasicHTML(body);
console.log(text); // => "Hello World"テスト中です。<html></html>
}
GASでも、メソッドの戻り値を利用し、メソッドチェーンでかく場面は非常に多いです。
/**
* Creates two time-driven triggers.
*/
function createTimeDrivenTriggers() {
// Trigger every 6 hours.
ScriptApp.newTrigger('myFunction')
.timeBased()
.everyHours(6)
.create();
// Trigger every Monday at 09:00.
ScriptApp.newTrigger('myFunction')
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(9)
.create();
}
配列の反復メソッドの戻り値が、配列なことを利用すること場面も多いです。
function myFunction8_60_03() {
const records = [
{ username: 'TSUJIKE', age: 25 },
{ username: 'YAMAGUCHI', age: 35 },
{ username: '', age: 100 },
{ username: 'KATAOKA', age: 31 }
];
const users = records.map(record => record.username)
.filter(username => !!username)
.map(username => username.toLowerCase()
);
console.log(users); //[ 'tsujike', 'yamaguchi', 'kataoka' ]
}
もし、自作ライブラリを作成したら、メソッドにはthis(結合されたオブジェクト自身)をreturnさせましょう。
とある、状態をもったオブジェクトが引数で渡された(ステートフルAPI)さい、ユーザーは流れるようなスタイル(fluent style)でコーディングできます。
まとめ
以上で、「一貫した規約を維持しよう」と「メソッドチェーンをサポートしよう」をお届けしました。
次回は、「undefinedは「値なし」として扱う」 をお届けします。
参考資料
このシリーズの目次
- [EffectiveJavaScript輪読会8]一貫した規約を維持しよう
- [EffectiveJavaScript輪読会8]undefinedは「値なし」として扱う
- [EffectiveJavaScript輪読会8]オプションオブジェクトで、キーワード付き引数群を受け取ろう
- [EffectiveJavaScript輪読会8]不必要な状態を排除する
- [EffectiveJavaScript輪読会8]柔軟なインターフェイスのために、構造的な型付けを使う
- [EffectiveJavaScript輪読会8]配列と「配列のようなもの」を区別しよう
- [EffectiveJavaScript輪読会8]過剰な型強制を防ごう