どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。
前回のおさらい
前回は、「undefinedは「値なし」として扱う」をお届けしました。
今日は3回目で、「オプションオブジェクトで、キーワード付き引数群を受け取ろう」をお届けします。
テキスト第6章「ライブラリとAPI設計」の項目55に対応しています。
今日のアジェンダ
- オプションオブジェクトとは
- オプションオブジェクトの仮引数
- extend関数
オプションオブジェクトとは
UrlFetchAppクラスのfetch()メソッドは、引数にurlとparamsを取ります。
この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 }
}
次回は、「不必要な状態を排除する」 をお届けします。
参考資料
このシリーズの目次
- [EffectiveJavaScript輪読会8]一貫した規約を維持しよう
- [EffectiveJavaScript輪読会8]undefinedは「値なし」として扱う
- [EffectiveJavaScript輪読会8]オプションオブジェクトで、キーワード付き引数群を受け取ろう
- [EffectiveJavaScript輪読会8]不必要な状態を排除する
- [EffectiveJavaScript輪読会8]柔軟なインターフェイスのために、構造的な型付けを使う
- [EffectiveJavaScript輪読会8]配列と「配列のようなもの」を区別しよう
- [EffectiveJavaScript輪読会8]過剰な型強制を防ごう