118
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

JavaScriptでメソッドチェーンを作る

はじめに

jQueryのメソッドチェーンはとても便利で、自分もよく使っていますが「作り方」が
よく分かっていなかったので実際に簡単なサンプルを作ってみました。
知っている人にとっては簡単だと思いますが、自分のように知らない人に少しでも
お役に立てれば幸いです(`・ω・´)ゞ

元記事 http://sawapi.hatenablog.com/entry/2014/12/05/004710

そもそもメソッドチェーンとは

メソッドチェーンとは、メソッドをつなげて処理することをいいます。
例えば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にあげました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
118
Help us understand the problem. What are the problem?