[EffectiveJavaScript輪読会8]オプションオブジェクトで、キーワード付き引数群を受け取ろう

GAS

どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。

前回のおさらい

前回は、「undefinedは「値なし」として扱う」をお届けしました。

[EffectiveJavaScript輪読会8]undefinedは「値なし」として扱う
どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。前回のおさらい前回は、「一貫した規約を維持しよう...

今日は3回目で、「オプションオブジェクトで、キーワード付き引数群を受け取ろう」をお届けします。

テキスト第6章「ライブラリとAPI設計」の項目55に対応しています。

今日のアジェンダ

  • オプションオブジェクトとは
  • オプションオブジェクトの仮引数
  • extend関数

オプションオブジェクトとは

UrlFetchAppクラスのfetch()メソッドは、引数にurlparamsを取ります。

このparamsのように、キーと値のペアで記述される引数のことを、オプションオブジェクトと呼びます。

function myFunction8_55_01() {

  const data = {
    'name': 'Bob Smith',
    'age': 35,
    'pets': ['fido', 'fluffy']
  };

  const options = {
    'method': 'post',
    'contentType': 'application/json',
    'payload': JSON.stringify(data)
  };

  //引数optionsのことをOptions Objectと呼ぶ
  UrlFetchApp.fetch('https://httpbin.org/post', options);

}

ライブラリを作成していくなかで、処理を抽象化していけばいくほど、引数の数は増えます

さらに、JavaScriptは、引数の順番を管理する必要がありますので、引数が増えると大変です。

//必ずバグを生みそうな引数群
const alert = new Alert(100,75,300,200,'Error','message','blue','white','black','error',true);

引数を、オプションオブジェクト形式にすると、コードを読むのも書くのも楽になります。

引数をオプションオブジェクトにするためには、それぞれの引数にプロパティを設定します。

function myFunction8_55_02() {

  const alert = new Alert({
    x: 100, y: 75,
    width: 300, height: 200,
    title: 'Error', message: 'message',
    titleColor: 'blue', bgColor: 'white', textColor: 'black',
    icon: 'error', modal: true
  });

}

もし、必須な引数が1個か2個あるばあいは、オプションオブジェクトとは別に渡すこともできる。

function myFunction8_55_02_02() {

  const alert = new Alert(app, message, {
    width: 150, height: 100,
    title: 'Error', 
    titleColor: 'blue', bgColor: 'white', textColor: 'black',
    icon: 'error', modal: true
  });

}

オプションオブジェクトの仮引数

オプションオブジェクトを受け取る、仮引数を見てみましょう。

デフォルト引数も使いながら、このような仮引数になります。

function myFunction8_55_03() {

  /** クラスAlert */
  class Alert {

    /** コンストラクタ
      * @constructor
      */
    constructor(parent, message, opts = {}) {
      this.width = opts.width === undefined ? 320 : opts.width;
      this.height = opts.height === undefined ? 240 : opts.height;
      this.x = opts.x === undefined ? (parent.width / 2) - (this.width / 2) : opts.x;
      this.y = opts.y === undefined ? (parent.height / 2) - (this.height / 2) : opts.y;
      this.title = opts.title || 'Alert';
      this.titleColor = opts.titleColor || 'gray';
      this.bgColor = opts.bgColor || 'white';
      this.textColor = opts.textColor || 'black';
      this.icon = opts.icon || 'info';
      this.modal = !!opts.modal;
      this.message = message;
    }

  }

  const parent = { width: 200, height: 500 };
  const message = 'test';
  const opts = {
    x: 100, y: 75,
    width: 150, height: 100,
    title: 'Error',
    titleColor: 'blue', bgColor: 'white', textColor: 'black',
    icon: 'error', modal: true
  };

  const a1 = new Alert(parent, message, opts);

  console.log(a1);
    /* => 
    { width: 150,
      height: 100,
      x: 100,
      y: 75,
      title: 'Error',
      titleColor: 'blue',
      bgColor: 'white',
      textColor: 'black',
      icon: 'error',
      modal: true,
      message: 'test' }
    */

}

ユーザーにとって、引数の順番によって引数が決まる関数(位置引数)とくらべて、読みやすく書きやすくなったと思います。

そして、さらにコーディングを楽にするテクニックをご紹介します。

extend関数

オプションオブジェクトの仮引数で、値の存在を確認していた処理をサブルーチン化します。

function myFunction8_55_04() {

  const extend = (target, source) => {

    if (source) {
      for (const [key, val] of Object.entries(source)) {
        if (typeof val !== undefined) target[key] = val;
      }
    }
    return target;
  
  };

  const target = { x: 100, y: '', width: 150, height: 100 };
  const source = { x: 50 };
  console.log(extend(target, source)); // => { x: 50, y: '', width: 150, height: 100 }

}

このextend関数により、さきほどのAlertコンストラクタの中身がスッキリしました。

function myFunction8_55_05() {

  /** クラスAlert */
  class Alert {

    /** コンストラクタ
      * @constructor
      */
    constructor(parent, message, opts = {}) {

      opts = this.extend({ width: 320, height: 240 }, opts);

      opts = this.extend({
        x: (parent.width / 2) - (this.width / 2),
        y: (parent.height / 2) - (this.height / 2),
        title: 'Alert',
        titleColor: 'gray',
        bgColor: 'white',
        textColor: 'black',
        icon: 'info',
        modal: false
      }, opts)

      this.extend(this, opts);
    }

    /** 仮引数の値をチェックするメソッド */
    extend(target, source) {
      if (source) {
        for (const [key, val] of Object.entries(source)) {
          if (typeof val !== undefined) target[key] = val;
        }
      }
      return target;
    }

  }


  const parent = { width: 200, height: 500 };
  const message = 'test';
  const opts = {
    x: 100, y: 75,
    width: 150, height: 100,
    title: 'Error',
    titleColor: 'blue', bgColor: 'white', textColor: 'black',
    icon: 'error', modal: true
  };

  const a2 = new Alert(parent, message, opts);

  console.log(a2);

  /* =>
    { x: 100,
    y: 75,
    title: 'Error',
    titleColor: 'blue',
    bgColor: 'white',
    textColor: 'black',
    icon: 'error',
    modal: true,
    width: 150,
    height: 100 }
  */

}

まとめ

以上で、「オプションオブジェクトで、キーワード付き引数群を受け取ろう」をお届けしました。

jQuery(ジェイクエリー)は、ウェブブラウザ用のJavaScriptコードを、より容易に記述できるようにするために設計された、JavaScriptライブラリです。

jQueryのajax()メソッドは、非同期処理(Ajax)リクエストを送信するオプションを、キーと値のペアで指定します。

まさに、オプションオブジェクトと呼ばれています。

  $.ajax({
    type: 'get',
    url: './data1.html'
  })

さらに、jQueryには、extend()メソッドが提供されており、オブジェクトを上書き結合します。

これぐらいの仕様なら、ライブラリを使うまでもなさそうですが。。。ご参考までに。

//このコードは動きません
function myFunction8_55_99() {

  const user1 = {
    name: '太郎',
    area: 'tokyo'
  }

  const user2 = {
    name: '花子',
    age: 28
  }

  //jQueryライブラリのextend()メソッド
  const result = $.extend(user1, user2);
  console.log(result); // => { name: "花子", area: "tokyo", age: 28 }
}

次回は、「不必要な状態を排除する」 をお届けします。

参考資料

このシリーズの目次

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