委譲 - JavaScriptの個人的なメモ
委譲の意味を整理
JavaScriptでは4つの意味を持った委譲があります。まず、あいまいさを避けるため、ここで、それを整理してみましょう。
ここでは、1の「オブジェクト指向プログラミング言語で使われている委譲」(以後、委譲と略す)の実装について、説明していきます。つまり、他のプログラミング言語を習得している方に向けたものです。個人的なメモですので、誤りがあるかもしれません。見つけたら、ご指摘ください。
サンプルのコード
さっそくですが、JavaScriptで委譲を実装してみましょう。
まず、二つのオブジェクト、aとsを用意して、オブジェクトsのhogeメソッドがaのhogeメソッドを参照するようにしてみます。以下のようにすれば、シンプルにできます。
function D() { this.a = 12; this.hoge = function() { return this.a; } }; function F() { var a = new D(); this.hoge = function() { return a.hoge(); }; }; var s = new F(); s.hoge(); // 12を出力
s.hogeメソッドは、オブジェクトaのhogeメソッドを呼び出しています。
このように、処理を他のオブジェクトに任せると、機能の拡張ができます(注1)。
bindメソッド
thisの遅延束縛 (注2) をキャンセルするbindメソッドを使うと、さらに簡単に委譲ができるようになります。たとえば、上のコードをbindメソッドを使って、書き直してみましょう。
function D() { this.a = 12; this.hoge = function() { return this.a; } }; function F() { var a = new D(); this.hoge = a.hoge.bind(a); //bindメソッドによって、thisが第一引数のaに固定されるような、関数オブジェクトを返す }; var s = new F(); s.hoge(); // 12
bindメソッドはthisとargumentsを固定することができるので、便利なメソッドです。thisの遅延束縛が面倒な人にぜひ、おすすめしたいです。
that = thisを使う
さらに、コンストラクタ関数 D にthat = thisを使うと、委譲と継承ができます。
委譲と継承の利点をまとめるために、ここでは比較として、両方のサンプルをコードとして書いておきます。Fが委譲で、F1が継承をしています。
function D() { var that = this; this.a = 12; this.hoge = function() { return that.a; }; }; function F() { var a = new D(); this.hoge = a.hoge; //Dに委譲 }; function F1() { D.apply(this, arguments); //Dを継承 }; var s = new F(); s.hoge(); // 12 s.a; // undefined var t = new F1(); t.hoge(); // 12 t.a; // 12
s.hogeメソッドはD::hogeなのに対して、t.hogeメソッドはF1::hogeとなり、F1由来のメソッドです。
プロトタイプ
prototypeプロパティを使うと、より強力な委譲を行うことができます。ただし、オブジェクトの「継承」という意味合いが強いです。
以下のコードでは、dのaプロパティも受け継いでいます。
function F() {}; var d = new D(); F.prototype = d; var s = new F(); s.hoge(); // 12 s.a; // 12 d.aプロパティだということに注意
一部の処理だけではなくて、オブジェクトをまるごと継承しているイメージでかまいません。「委譲」と区別するために、冒頭では、2の「プロトタイプチェインを使った委譲」として別に分けています。
その他、ネットで調べると、いろいろな委譲を実現した手法に出会うのですが、長くなりそうな解説は他の記事に譲ります。
結論
- 他のプログラミングと違って、JavaScriptでは、一口に委譲といってもいろいろあるよ
- 他のオブジェクトに任せると、拡張したいときに楽