オブジェクト関数・メソッド

【JavaScript】 メソッドチェーンを本質的に理解する

更新日:2021/08/02

JavaScriptにはメソッドチェーンという機能があります。
多くの人は使っているうちに理解していきますが、初心者のうちは少し難解に感じると思います。

そこでここでは、メソッドチェーンについて噛み砕いて解説していきます。

 

メソッドチェーンとは

メソッドチェーンとは、メソッドの実行結果に対して変数などを仲介せずに、直接他のメソッドを実行することを指します。
JavaScriptではメソッドを「 .」(ドット)で連結します。

次の例は配列の各要素を3倍した結果から2で割り切れるものを抽出し、最後に文字列化しています。
このコードは少し非効率です。

少し非効率なコード


const a = [ 1 , 2 , 3 , 4 ];

const result = a.map( value=>value*3 );
const result2 = result.filter( value => value % 2 === 0 );
const result3 = result2.join( "," );

console.log( result3 ); // 6,12

ここでは変数を3つも定義しています。
結果を受け取る変数をletで指定して、同じものを使いまわしてもいいのですが、僕の場合できる限りletを使用しない方針でコードを作成しています。

しかし変数が乱立するのも、避けたいところです。
そのため、上のような場面ではletかconstか非常に迷います。

ですが、次のようにメソッドチェーンを使用すると、迷う必要がありません。

メソッドチェーンの例


const a = [ 1 , 2 , 3 , 4 ];

const result = a.map( value=>value*3 ).filter( value => value % 2 === 0 ).join( "," );

console.log( result ); // 6,12

変数が一つで済み、非常にスッキリとしました。
メソッドが途切れることなくつながっていることで、一連の処理であることが視覚的に読み取れるのも、メソッドチェーンの効果です。

また、最近よく見かけるようになったPromise...thenの組み合わせもメソッドチェーンです。

メソッドチェーンの例(Promise)


new Promise((resolve, reject) =>setTimeout( ()=> resolve(5), 5000))
    .then( (msec) =>console.log(msec+"秒経過しました") )
    .catch( () => console.error("エラー"));

Promiseにはプロミスチェーンという言葉もあるので、区別する必要があります。

Promiseについては、こちらをご覧ください。
【JavaScript】 非同期はPromise?解説が難しいので自分で理解してみた

 

メソッドチェーンの本質

もう少し詳しくメソッドチェーンについて解説していきます。

メソッドチェーンのよくある勘違い

次の例は配列の各要素を3倍した結果から2で割り切れるものを抽出して文字列化し、最後に数値を全角に変換しています。


const a = [ 1 , 2 , 3 , 4 ];

const result = a.map( value=>value*3 )
    .filter( value => value % 2 === 0 )
    .join( "," )
    .replace(/\d/g,match=>["0","1","2","3","4","5","6","7","8","9"][match]);

console.log( result ); // 6,12

僕がJavaScriptを学び始めた頃にこのようなコードを見たとき、最初の配列aが各メソッドで処理されていると思いました。

これは大きな勘違いです。

配列aを処理しているのは、最初のmapメソッドだけです。

以降のメソッドは、直前のメソッドの戻り値を処理しています。

メソッドチェーンは各メソッドの戻り値の確認が必須

メソッドチェーンを使用したプログラムコードを作成するには、各メソッドがどのような値を戻しているのかを確認する必要があります。

上記の例では、mapメソッドは配列を返します。

mapメソッドは配列を返す

filterメソッドはArrayオブジェクトのメソッドなので、mapメソッドの結果とメソッドチェーンできます。

filterメソッドは配列を返します。そのため同様に、filterメソッドの結果からjoinメソッドをチェーンできます。

filterメソッドは配列を返す

次がこの例の重要な部分です。

joinメソッドは文字列を返します。
そのため、mapなどのArrayオブジェクトのメソッドを呼び出すことができません。

joinメソッドは文字列を返す

その代わりに、Stringオブジェクトのメソッドを呼び出すことができます。
ここでは、Stringオブジェクトのreplaceメソッドを呼び出し、文字の置き換えをおこなっています。

このように、各メソッドの戻り値を確認しながら、メソッドチェーンを構築していいきます。

 

自作オブジェクトでのメソッドチェーン

自作オブジェクトでメソッドチェーンをおこなうには、関数内でthisを戻します。

thisは通常はオブジェクトそのものを指すので、戻り値から所属するメソッドを呼び出すことができます。

単純なオブジェクトでの例

次の例は、メソッドチェーンを考慮した自作オブジェクトです。

メソッドチェーンを考慮した自作オブジェクト


const obj = {
    value:5,
    method1: function(){
        this.value = this.value * 2;
        return this;
    },
    method2: function(){
        this.value = this.value * 3;
        return this;
    },
    method3: function(){
        this.value = this.value * 3;
        return this.value;
    },

      // このメソッドは期待通りの動作をしません
    method4: () => {
        this.value = this.value * 4;
        return this.value;
    },
};

アロー関数は関数内でthisを所持していません。
そのため、メソッドチェーンを目的とした関数では使用できません。
必ずfunctionで関数を作成します。

アロー関数については、次のページを読んでください。
【JavaScript】 アロー関数は何者!?かっこいいだけじゃない!

この自作メソッドは、次のようにメソッドチェーンが可能です。

自作オブジェクトでメソッドチェーン


const result = obj.method1().method2().method3();
console.log( result ); // 90

なお、method3()の戻り値は数値のため、これ以降は自作オブジェクトのメソッドを呼び出すことができないことに注意が必要です。

プロトタイプを意識したオブジェクトでの例

プロトタイプチェーン経由でのメソッドチェーンを行う時は、次のようにコンストラクターを作成して、protorypeプロパティにメソッドを定義します。

作成したコンストラクターからnewでインスタンスを作成すれば、プロトタイプを意識せずにメソッドチェーンをおこなうことができます。

インスタンス経由でのメソッドチェーン


const obj = function ( value ){
    this.value = value;
};
obj.prototype = {
    method1: function(){
        this.value = this.value * 2;
        return this;
    },
    method2: function(){
        this.value = this.value * 3;
        return this;
    },
    method3: function(){
        this.value = this.value * 3;
        return this.value;
    }
};

const result = new obj( 5 ).method1().method2().method3();
console.log( result ); // 90

コンストラクターやプロトタイプについては、別の記事で解説しているのでそちらを参照してみてください。

classでオブジェクトを定義する例

classでのオブジェクト定義したときの、メソッドチェーン例です。

classでのメソッドチェーン


class obj {
    constructor( value ) {
        this.value = value;
    }
    method1(){
        this.value = this.value * 2;
        return this;
    }
    method2(){
        this.value = this.value * 3;
        return this;
    }
    method3(){
        this.value = this.value * 3;
        return this.value;
    }
}

const result = new obj( 5 ).method1().method2().method3();
console.log( result );

クラスはコンストラクターを使用してオブジェクト定義を、他の言語のクラス風に置き換えたものです。
クラスについては、別の記事で解説しているのでそちらを参照してみてください。

更新日:2021/08/02

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。