メソッドチェーンの作り方
メソッドチェーンはわかりやすくて便利です。jQueryなどではメソッドチェーンが効果的に使えるように設計されているので、jQueryでメソッドチェーン使用している人も多いと思います。
このメソッドチェーンですが、作り方を解説するページがあまりない気がするので、JavaScriptを例にメソッドチェーンの作り方を紹介してみようと思います。
メソッドチェーンの原理
メソッドチェーンとは、名前のとおり、メソッドを実行して、その結果に対してさらにメソッドを実行する感じで、メソッドを繋げながら何らかの処理をしていく仕組みです。
例
hoge().fuga().piyo();
メソッドで返す値がメソッドを持っていることがメソッドチェーンを実行できる条件になります。
JavaScriptには最初からメソッドチェーンが実行できるメソッドがある
JavaScriptには最初からメソッドチェーンが実行できるメソッドがあります。
配列が持っている一部のメソッドなどが該当します。具体的には配列を返すメソッドです。
配列のメソッドでメソッドチェーンしてみる
以下のコードを見てみましょう。
var arr = [1,2,3,4,5]; arr.slice(0,4);
最初の行で、変数arrは1〜5の値をそれぞれのアドレスに格納した配列と紐付けられました。
arrは配列に紐付けられたので、すぐさま配列のメソッドが使用できるようになります。push, shift, slice, join, 他。
次の行で使用しているsliceメソッドは、第1引数に開始位置、第2引数に終了位置を指定することで、指定した箇所にある値をまとめた新しい配列を返すメソッドです。
返り値は配列なので、この場合は、[1,2,3,4]が返ってきます。
この返り値がポイントです。
配列が返ってくるわけですから、配列が持っているメソッドも一緒に返ってくることになります。
なので、この場合は、メソッドチェーンが実行できます。
var arr = [1,2,3,4,5]; arr.slice(0,4).slice(0,3);
このコードを実行すると[1,2,3]という配列が返ってきます。
arr.slice(0,4)で[1,2,3,4]が返ってきて、次のslice(0,3)で[1,2,3]が返ります。
帰ってきた[1,2,3]も配列なので、もう一度配列のメソッドを実行することができます。
この例のように、返り値がメソッドを持っていさえすれば、メソッドチェーンを実行することができます。
pushメソッドをメソッドチェーンに対応させてみる
では、同じ配列のpushメソッドを実行してみます。
pushメソッドは配列の最後尾に値を追加するメソッドです。
var arr = [1,2,3]; arr.push(4);
では、このpushメソッドは以下のようなメソッドチェーンを実行することができるでしょうか?
var arr = [1,2,3]; arr.push(4).push(5);
答えはNoです。
pushメソッドの返り値は、pushした値配列の要素数です。引数にした4という値は配列ではありませんので、返ってくる要素数の4を使って、pushメソッドを続けて実行することができません。
上記のようなpushメソッドをメソッドチェーンにするためには、pushメソッドを持った値(配列オブジェクト)を返さなければなりません。
ちょっとお行儀が悪いですが、配列オブジェクトの中にpushし終わった配列を返す、repushメソッドを追加してみます。
Array.prototype.repush = function(value/* ..., valueN */) { if (arguments.length === 0) { return this; } else { this.push(value); } if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { this.push(arguments[i]); } return this; } else { return this; } };
このコードで出てくるthisは、メソッドを実行した配列です。通常のpushメソッドを実行し、値を追加したthis(配列)を返しています。返り値が配列なので、メソッドをつなげることができます。
var arr = [1,2,3]; arr.repush(4).repush(5).repush(6,7,8).repush(9);
うまくいきます。
自分の作ったオブジェクトでメソッドチェーンを使えるようにする
では、自分の作ったオブジェクトにメソッドチェーンを追加してみましょう。
HTMLのp要素を連続して出力するメソッドを作ってみます。
以下のような仕様にします。
- コンストラクタの引数でp要素を追加する要素を指定する
- textメソッドの引数で段落の文章を入力する
- brメソッドで改行を入力する
- appendメソッドで文章と改行を含んだ段落を生成し、追加する
var P = function(element) { this.element = element; this.paragraph = []; }; P.prototype = { text : function(value) { this.paragraph.push(document.createTextNode(value)); return this; }, br : function() { this.paragraph.push(document.createElement('br')); return this; }, append : function() { var p = document.createElement('p'); for (var i = 0; i < this.paragraph.length; i++) { p.appendChild(this.paragraph[i]); } this.element.appendChild(p); this.paragraph = []; return this; } };
以下のように使います。
var target = document.getElementById('hoge'); var p = new P(target); p.text('昔々あるところに').br() .text('おじいさんとおばあさんが住んでいました').append() .text('おじいさんは山へ芝刈りに').br() .text('おばあさんは川へ洗濯に行きました').append();
使いやすいかどうかは置いといて、これで自分の作ったオブジェクトでメソッドチェーンが使えるようになりました。
ポイントは、常に最後でthisを返していることです。thisを返すことによって、定義したプロパティとメソッドをそのまま返すことができます。