Node.js(JavaScript) でのクラスの定義について考え直してみた
ども、@kimihomです。
毎度 JavaScript でオブジェクト指向プログラミングをやろうとすると、どうやって書けばいいか悶々としていたが、最近ようやく定まってきたのでまとめてみる。調べるとnewを使ったり、prototypeを使ってメソッドを定義したりする方法もあるようだが、個人的には以下の方法が一番書きやすいし理解しやすかった。
JavaScript でのクラス定義
JavaScript はご存知の通り classキーワードが存在しない。そのため function オブジェクトを使って定義することになる。この実装でキーとなるのはメソッドの返り値にオブジェクトを返すところにある。
var MyClass = function(args) {
var pVar = "Private 変数";
var args = args; // コンストラクタの初期化
var privateFunc = function() {
//プライベート関数
};
return {
publicFunc: function() {
// パブリック関数。 privateFuncや pVar, args など参照できる。
},
publicFunc2: function() {
this.publicFunc(); // this が必要。
}
};
};
module.exports = MyClass;
//
// 別ファイルで
var myClass = require('./myClass')(args); // ()付き!
上記のコードを見ると、なぜパブリックなメソッドとプライベートなメソッドがこれで定義できるのかも理解しやすいと思う。MyClass()を呼び出した時、返ってくるのはJavaScript のオブジェクトになる。つまり、別ファイルで定義したmyClassにはpublicFuncを持ったオブジェクトが返ってくることになる。 JavaScript のクロージャの仕組みによって、publicFunc()のコードの中にprivateFuncなどのメソッドを参照することができる。これにより、private, public なメソッドを定義することが可能になる。
(余談) クロージャって言葉を書くとそれなりに中・上級者っぽく見えるw。JavaScriptのapplyとか使いこなせるようになると、そのさらに上ってイメージ。
この方法の注意点
注意する必要があるのが2つある。
1つ目は、 Node.js で require した時、最後にメソッド呼び出しとして () をつける必要がある点だ。これでメソッド呼び出しをした戻り値(オブジェクト)がmyClassに格納されることになる。
2つ目は、publicメソッドの中でpublicメソッドを呼ぼうとすると、"そんなメソッドねーよ"とエラーになる点だ。// this が必要というコメントの部分にあたる。これは publicFunc や publicFunc2 JavaScript の オブジェクトで定義された値でしかないので、直接名前だけで呼び出すことができないため、thisを明示的に示さないとメソッドを見つけることができないのが原因だ。
この this ってのも曲者で、例えばpublicメソッドの中で無名関数を呼ぶと、そのthisの指すオブジェクトも変わってしまう。以下のような場合だ。
publicFunc2: function() {
this.publicFunc(); // this が必要。
[1, 2, 3].forEach(function(val, index) {
// this.publicFunc();
});
}
forEach で括られた中の this は、MyClassで返すオブジェクトではない。forEachの中のオブジェクトを指してしまうので上記コードは正しく動作しない。てことでここでもクロージャを使ってthisの別名を作成する必要がある。
publicFunc2: function() {
this.publicFunc(); // this が必要。
var self = this;
[1, 2, 3].forEach(function(val, index) {
self.publicFunc(); //OK!
});
}
this は無名関数の中に入ると気ままに向き先を変えてしまうので、注意しよう。
終わりに
それなりに大きな JavaScript プログラミングをするなら、オブジェクト指向でやらないと大変な目にあうだろう。JavaScirptコードが大きくなってきて、リファクタリングが必要だと感じられるようになってきた時に、このやり方を知っておけば JavaScript でも効率的なオブジェクト指向プログラミングが可能になると思う。
クラスって意味では今回紹介した方法はわかりにくい(継承がない)けども、JavaScript の性質をうまく利用したクラス定義方法であると思う。むしろ継承しにくい方がいいと思う。移譲、移譲、移譲って言われてるし。
てことで JavaScript でも Let's オブジェクト指向プログラミング!