あと味

たくさん情報を食べて、たくさん発信すると、あとになって味わい深い。

メソッドチェーンの作り方

メソッドチェーンはわかりやすくて便利です。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を返すことによって、定義したプロパティとメソッドをそのまま返すことができます。

まとめ

jQueryがメソッドチェーンを使える理由は、常に各メソッドで、jQueryオブジェクトを返しているからです。仕組み自体は単純ですが、強力な手法ですね。

ということで、メソッドチェーンの作り方講座を終わります。