[EffectiveJavaScript輪読会8]一貫した規約を維持しよう

GAS

どうも。つじけ(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, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&apos;');
  }

  const body = '"Hello World"テスト中です。<html></html>';
  const text = escapeBasicHTML(body);
  console.log(text); // => &quot;Hello World&quot;テスト中です。&lt;html&gt;&lt;/html&gt;

}

メソッドチェーンでかくと、このように、途中経過の値を代入する変数を用意する必要がありません。

function myFunction8_60_02() {

  function escapeBasicHTML(str) {

    const text1 = str.replace(/&/g, '&amp;');
    const text2 = text1.replace(/</g, '&lt;');
    const text3 = text2.replace(/>/g, '&gt;');
    const text4 = text3.replace(/"/g, '&quot;');
    const text5 = text4.replace(/'/g, '&apos;');

    return text5;
  }

  const body = '"Hello World"テスト中です。<html></html>';
  const text = escapeBasicHTML(body);
  console.log(text); // => &quot;Hello World&quot;テスト中です。&lt;html&gt;&lt;/html&gt;

}

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は「値なし」として扱う」 をお届けします。

参考資料

このシリーズの目次

タイトルとURLをコピーしました