JavaScriptでメソッドチェーンを作る
はじめに
jQueryのメソッドチェーンはとても便利で、自分もよく使っていますが「作り方」が よく分かっていなかったので実際に簡単なサンプルを作ってみました。 知っている人にとっては簡単だと思いますが、自分のように知らない人に少しでも お役に立てれば幸いです(`・ω・´)ゞ
そもそもメソッドチェーンとは
メソッドチェーンとは、メソッドをつなげて処理することをいいます。 例えばjQueryだと、
jQuery( 'div' ).width( 100 ).height( 100 ).css( { backgroundColor: '#000' } );
といったようにwidth, height, cssのメソッドをつなげて書くことができます。 もし、jQueryがメソッドチェーンに対応していないと以下の様な形で書かないといけません。
var div = new jQuery( 'div' ); // 内部的にnewしてるのでつけるかどうか微妙だけど。 div.width( 100 ); div.height( 100 ); div.css( { backgroundColor: '#000' } );
一度変数に入れることが必須となるので少しめんどくさいですね。
メソッドチェーンの基本的な作り方
作り方の前にまずは、文字列をappendメソッドで連結し、getメソッドで連結した文字列を取得する メソッドチェーンに対応していないクラスを作ってみます。
var builder = function( str ) { if ( typeof str === 'undefined' ) { this._str = ''; } else { this._str = '' + str; // 型変換 } }; builder.prototype = { // 文字連結 append: function( str ) { this._str += '' + str; }, // 連結した文字列を返す get: function() { return this._str; } }
上記のクラスを使うと
var b = new builder(); b.append( 'hoge' ); b.append( 'fuga' ); b.append( 'moge' ); var result = b.get(); console.log( result ); // > hogefugamoge
このような形で書くことになります。 さて、このbuilderクラスをメソッドチェーンできるようにしてみましょう。
var builder = function( str ) { if ( typeof str === 'undefined' ) { this._str = ''; } else { this._str = '' + str; // 型変換 } }; builder.prototype = { // 文字連結 append: function( str ) { this._str += '' + str; // 自分自身を返す return this; }, // 連結した文字列を返す get: function() { return this._str; } }
このクラスは以下のように利用することができます。
var result = new builder().append( 'hoge' ).append( 'fuga' ).append( 'moge' ).get(); console.log( result ); // > hogefugamoge
メソッドをつなげて書くことが出来ました! どこが変わったかというと、appendメソッドで「return this;」として自分自身を返してあげています。 これだけでメソッドチェーンに対応できるんですね! ただし、これではbuilderの前にnewがついてしまっています。newを取る方法はいろいろあるような 気がしますが、ここではjQueryのやり方にならって書いてみます。
「new」を取る
では、実際にnewをつけない書き方で書いてみましょう。
var builder = function( str ) { // インスタンスをbuilderの中で生成 return new builder.prototype.init( str ); }; builder.prototype = { // 初期化メソッド( initという名前じゃなくてもいいです ) init: function( str ) { // コンストラクタ的なのをbuilder.prototype.initの中に書く if ( typeof str === 'undefined' ) { this._str = ''; } else { this._str = '' + str; // 型変換 } }, // 文字連結 append: function( str ) { this._str += '' + str; // 自分自身を返す return this; }, // 連結した文字列を返す get: function() { return this._str; } } // builder.initのprototypeにbuilderのprototypeを持たせる builder.init.prototype = builder.prototype;
これでnewをつけずに書くことができます!
var result = builder().append( 'hoge' ).append( 'fuga' ).append( 'moge' ).get(); console.log( result ); // > hogefugamoge
ポイントとしては、builderの中で書いていたコンストラクタ的な処理を自身のprototypeの中に 作った関数( ここではinit )にやらせてしまい、その関数のprototypeにbuilderのprototypeを 持たせ、builderの中でインスタンスを生成することでnewを省略することができます。 さて、ここまで分かったところで簡単な電卓クラスを作ってみます。
簡易電卓クラス
たす、ひく、かける、わるのできる簡単な電卓クラスを作ります。 ただし、カッコのついた計算もできるように少し工夫します。
!function( window ) { // 簡易電卓クラス // 3 * ( 1 + 3 ) / 6 // みたいな式を // calc( 3 ).multiply( calc( 1 ).plus( 3 ) ).divide( 6 ).get(); // こんな感じで使えるように var calc = function( num ) { // calcクラスであればそのまま返す if ( num instanceof calc ) { return num; } // 数字以外は0にする if ( typeof num !== 'number' ) { num = 0; } // インスタンス生成 return new calc.prototype.init( num ); } calc.prototype = { // コンストラクタ init: function( num ) { this._num = num; }, // たす plus: function( num ) { this._num += calc( num ).get(); return this; }, // ひく minus: function( num ) { this._num -= calc( num ).get(); return this; }, // かける multiply: function( num ) { this._num *= calc( num ).get(); return this; }, // わる( 0でわることを考慮してない ) divide: function( num ) { this._num /= calc( num ).get(); return this; }, // 取得 get: function() { return this._num; } } // initのprototypeにcalcのprototypeをつっこむ calc.prototype.init.prototype = calc.prototype; window.calc = calc; }( window );
さて、このクラスを使って簡単な計算をしてみましょう。
// 10 + 20 calc( 10 ).plus( 20 ).get(); // > 30 // ( 9 - 7 ) * 5 calc( 9 ).minus( 7 ).multiply( 5 ).get(); // > 10 // 3 * ( 1 + 3 ) / 6 // 括弧内の計算はcalcでおこないそのオブジェクト自体を引数に指定する calc( 3 ).multiply( calc( 1 ).plus( 3 ) ).divide( 6 ).get(); // > 2
ちょっと工夫すると括弧付きの計算もできるようになりました!
まとめ
メソッドチェーンするには、メソッドの最後に「return this;」とつけて自分自身を返すことで 簡単に作ることができました。使いどころはライブラリとか作らない限りあまりないかもしれませんが 覚えておいて損はなさそうです! calc.jsをgistにあげました。