読者です 読者をやめる 読者になる 読者になる

『Effective JavaScript』を読んで(項目53~60)

第6章は「ライブラリと API 設計」。

ライブラリを設計するときに気をつけるべきことが書いてある。 Coursera を始めたせいもあってか、全体的にさらっとまとめてしまった・・・。

項目53:一貫した規約を維持しよう

関数名、変数名、引数の順序など、一貫した規約を維持するようにする。利用する単語なども統一しておく。

項目54:undefined は「値なし」として扱う

JavaScript ではいろいろな場面で undefined に遭遇する。

// 代入されていない変数の値
var x;
x; // => undefined

// 存在しないプロパティへのアクセス
var obj = {};
obj.x; // => undefined

// 値なしリターン
function f1 () { return; } 
function f2 () { }
f1(); // => undefined
f2(); // => undefined

// 実引数が提供されないパラメータの値
function f(x) {
    return x;
}
f(); // => undefined

これらの値は「値なし」の場合で使うべきで、アプリケーション/ライブラリごとに特別なセマンティクスを持たせないように注意しよう。

項目55:オプションオブジェクトで、キーワード付引数群をうけとろう

項目 53 で述べたように関数やメソッドの引数の順序を揃えることは需要だが、大量の引数を必要とする場合にはオプションオブジェクトを利用しよう。 オプションオブジェクトを利用すると、引数指定に名前を利用できるだけでなく、デフォルトパラメータの呼び出しも分かりやすくなる。

// オプションオブジェクトの利用
var alert = new Alert({
    parent: app,
    width: width, height: height,
    title: "This is Error Message",
    titleColor: "blue", bgColor: "white", textColor: "black",
    icon: "error", moda: true
});

// デフォルトパラメータの利用
var alert2 = new Alert({});

この場合の Alert 関数の実装は以下のようになる。width と height が 0 を許容するため、undefined をチェックしていること、title などは空文字列を許容するため真偽値比較をしている点に注意する。

function Alert(options) {
    opts = opts || {};
    this.width = opts.width === undefined ? 300 : width;
    this.height = opts.height === undefined ? 200 : height;
    this.title = opts.title || "Default Title";
    ... (省略) ...
}

さらに Alert の実装を簡単にするため、多くのライブラリやフレームワークが提供している拡張(extend)を利用しよう。 これは target オブジェクトのプロパティと source オブジェクトのプロパティをマージする関数である。 これを利用して「オプションオブジェクトのデフォルト値とユーザーから提供されたオプションの値を併合する機能」を実装することができる。

function Alert(options) {
    opts = extend({
        width: 300,
        height: 200,
        title: "Default Title"
        ... (省略) ...
    }, opts)
    this.width = opts.width;
    this.height = opts.height;
    this.title = opts.title;
    ... (省略) ...
}

なお、extend 関数が提供されていない場合には「プロパティが undefined だったら src から dst に値をコピーする」という素朴な実装でも問題ない。

項目56: 不必要な状態を排除する

API はステートフルな API とステートレスな API に別れるが、一般的にはステートレス API の方が分かりやすいのでなるべくステートレスで実装する。

項目57:柔軟なインタフェースのために構造化された型付きを使う

JavaScript ではダックタイピングが利用できるから無理して継承する必要ないよ、というお話。 動的型付言語だからダックタイピングが可能。静的型付言語だからダックタイピングができない。という命題が正しいかは自分の知識ではよく分からないなぁ…。

話がそれるけど、よく見る動的型付言語⇔静的型付言語の議論は、よくよく見るとスクリプト系(LL とか呼ばれる系)言語⇔コンパイル系言語の議論に置きかわっていることが多いような気がする。コンパイルで型チェックをやることが多い(?)からな気がするけど。。。

項目58:配列と「配列のようなもの」を区別しよう

メソッドオーバーロードを利用するとき、不確実な推論で型を推測しないようにしよう。 JavaScript において配列と「配列のようなオブジェクト」を区別するには ES5 の Array.isArray を利用する。

オブジェクトがあるプロパティを実装しているかでその型を推論することを「ダック・テスティング(Duck Testing)」と呼んで非推奨な行為である。

項目59:過剰な型強制を防ごう

JavaScript では型強制が自動で行われるので、防御的プログラミングを行って型強制を防いで例外を送出すると有用な場合が多い。メソッドオーバーロードを利用する場合は特に有用である。

防御的プログラミングの実装例としてガードオブジェクトがあげられる。 本には実例が載っているのだがまとめる気力がなくなったのでこの節はこれで終了。。。

項目60:メソッドチェーンをサポートしよう

メソッドチェーンをサポートすると API として使いやすくなるのでできるだけサポートしよう。 なお、名前の元ネタは SmallTalkメソッドカスケードという構文らしい。

// 文字列置換のメソッドチェーンの実例
"aaa".replace("a", "b").replace("b", "c").replace("c", "d"); // => "ddd"

これで、後は「第7章:並行処理」だけになった。