綺麗に死ぬITエンジニア

JavaScriptでnew演算子を忘れても動作するコンストラクタを作る方法

2017-04-05

JavaScriptでインスタンスを作る際、通常はnew演算子をつけてインスタンスを生成します。

しかし、コンストラクタにnewをつけずともインスタンスを生成したい場合や、newがなくても同様に動作させたい場合が、JavaScriptで特にライブラリを作成している場合にはよく出てきます。

jQuery等多くのライブラリのように、いちいちnewをつけずにインスタンスを生成して、メソッドチェーンで処理を簡単に記述させたい場合などが当てはまります。

また、ライブラリ利用者が誤ってnew演算子をつけずに利用してしまって、予期せぬバグを実装させてしまう原因になってしまうこともあるでしょう。ドキュメントを読まない利用者が悪いのは確かですが、それでもバグになりそうな部分は淘汰しておきたいものです。

そんなとき、手軽にnewをつけずともnewをつけた場合と同様の動作になるようにコンストラクタを実装する方法を紹介します。

この方法を利用すれば、手軽に既存のコンストラクタをnewが不要なコンストラクタにすることができます。

実装方法

通常、コンストラクタ及びそのメソッドは以下のように実装するでしょう。

function Dog(name) {
    this.name = name;
}

Dog.prototype.bark = function () {
    return 'ワンワン!僕の名前は' + this.name + 'だワン!';
};

var pochi = new Dog('ポチ');
alert(pochi.bark());
// ワンワン!僕の名前はポチだワン!

コンストラクタの最初に次の5行を加えるだけで、new不要のコンストラクタが出来上がります。

if (!(this instanceof コンストラクタ)) {
    var bindParams = [コンストラクタ];
    Array.prototype.push.apply(bindParams, arguments);
    return new (Function.prototype.bind.apply(コンストラクタ, bindParams));
}

上記のDog()コンストラクタで実装した場合は、以下のようになります。

function Dog(name) {
    if (!(this instanceof Dog)) {
        var bindParams = [Dog];
        Array.prototype.push.apply(bindParams, arguments);
        return new (Function.prototype.bind.apply(Dog, bindParams));
    }
    this.name = name;
}

Dog.prototype.bark = function () {
    return 'ワンワン!僕の名前は' + this.name + 'だワン!';
};

var pochi = Dog('ポチ');  // new不要
alert(pochi.bark());

Dog('クロ').bark();  // メソッドチェーンで1行でメソッドを呼び出すことも可能

なお、この方法を用いても、従来どおりnewを用いてインスタンスを生成することが可能です。

まとめ

newを使わずにインスタンスが生成できるコンストラクタができました。

メソッドチェーンを用いたライブラリなど、いちいちnewを書くのが手間なコンストラクタ実装の際には、利用してみてはいかがでしょうか。

今回追記した5行の処理はそこまで難しいことはしていないので、場合によっては各ライブラリに合わせて改良して実装されると良いかもしれません。