Javascriptのオブジェクト指向
「Javascriptのオブジェクト指向は気持ち悪い!」
みたいな意見を聞くことがあるけれど、
そういうのはたいてい慣れの問題だと思っています。
「オレオレオブジェクト指向」を作って使っちゃうのも、
自分ひとりだけならいいけど多人数が見るコードでやるのは迷惑かなと。
だいたい「オレオレクラス」を分かりやすいと思っているのは作った人だけだったり。。。
でもJavascriptのオブジェクト指向に問題があるのは事実だし
対策は必要なので自分がベストだと思っている方法を書いておきます。
newをつけなくても呼び出せちゃう問題
function Point(x, y){ this.x = x; this.y = y; } var o = new Point(0, 0); // 問題なし var p1 = Point(1, 1); // p1はundefinedだし、グローバルオブジェクトが汚染される!
この場合、Pointコンストラクタの一行目に
console.assert(this instanceof Point, 'newをつけずに呼び出された可能性があります');
みたいに書いておくのがいいと思います。
Node.jsではエラーになるし、ブラウザでもコンソールを見ればわかります。
この問題への対策として
if(!(this instanceof Point)) return new Point(x, y);
みたいなコードも見るけれどあまりよくないです。
根本的な解決(newを付けてコールさせるべき)になっていないうえに、
可変長の引数を取る場合Function.prototype.bindを使う必要があって面倒臭さいです。
prototype継承とインスタンスの問題
問題のないprototypeチェーンを作るにはこのようにする必要があります。
毎回こんなことをやるのは面倒です。
だからといってオレオレクラスを作るのはよくないので、
コンストラクタに何もさせないことにします。
コンストラクタが何もしないのならば周りくどいことをする必要はありません。
function Point(){ console.assert(this instanceof Point, 'newをつけずに呼び出された可能性があります'); } Point.prototype.init = function(x, y){ // 初期化はこの関数で this.x = x; this.y = y; } Point2.prototype = new Point(); function Point2(){ Point.call(this); } var p1 = new Point(); p1.init(1, 2); // 初期化 var p2 = new Point2(); p2.init(1, 2); // 初期化
これはこれでデメリットがないとは言い切れませんが、大きな問題はないと思います。
DOMの操作で似たようなことをしますし。おすし。
var event = document.createEvent('Events'); event.initEvent('eventtype', false, false); // 初期化
ぶっちゃけprototypeって打つのメンドクサイ問題
Object.definePropertiesを使えるならば使いましょう。
prototypeって打つのは1回だけだし、for( in ) で列挙されないようにできます。
書き換え不能にもできます。
一石三鳥です。
Object.defineProperties(Point.prototype, { init: { value: function(x, y){ this.x = x; this.y = y; } }, distance: { value: function(otherPoint){ var x = otherPoint.x - this.x, y = otherPoint.y - this.y; return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); } } });
でも、そもそもfunctionって打つのも面倒なので、
根本的に解決したいなら補完してくれるエディタを使うべきだったり。(Aptanaとか)
まとめ
「オレオレオブジェクト指向」で作ったコードを読めるのは「今」の自分だけかもしれません。
すみやかにやめたらいいと思います。
どうしても素のJavaScriptが嫌なら「traceur」みたいなものがあるんだから、
そっちを使えばいいんじゃないでしょうか。