前回はReveling Module Patternについて書きました。JavaScriptでもprivateとpublicの領域を明快に分けられるという利点がありましたが、複数のインスタンスを作る際に、privateな関数もpublicな関数ももろもろが複製されるので、あまり効率が良くなさそうというのが懸念でした。
そこで、その点を補ったのがReveling Prototype Pattern(リビーリングプロトタイプパターン)ということで、紹介記事を読んで試してみました。
Revealing Prototype Patternによるオブジェクトの作り方
//最初にコンストラクタを定義 var MessageCard = function(decorationChar) { this.decoration = decorationChar; this.history = []; } //プロトタイプに機能を追加 MessageCard.prototype = function() { function create(to, message){ var head = createHead(to); var body = createBody(message, this); var contents = head + body; this.history.push(contents); return contents; } function createHead(to){ return 'Dear ' + to + '\n'; } function createBody(message, thisObj){ var line = createLine(message.length + 4, thisObj.decoration) + '\n'; var body = line; body += thisObj.decoration + ' ' + message + ' ' + thisObj.decoration + '\n'; body += line; return body; } function createLine(length, char){ var line = ''; for(var i = 0; i < length; i++){ line += char; } return line; } function getHistory(index){ return this.history[index]; } return { create: create, getHistory: getHistory }; }(); var messageCard = new MessageCard('+'); console.log(messageCard.create('tanaka', 'hello')); /* Dear tanaka +++++++++ + hello + +++++++++ */ console.log(messageCard.getHistory(0)); /* Dear tanaka +++++++++ + hello + +++++++++ */
最初にコンストラクタを用意して、そのプロトタイプを拡張するみたいですね。 少々厄介なところがthisの参照を引き回す箇所がある点です。
function create(to, message){ var head = createHead(to); var body = createBody(message, this); //thisを引き回す var contents = head + body; this.history.push(contents); return contents; } function createBody(message, thisObj){ //オブジェクトの参照を引数から受け取る var line = createLine(message.length + 4, thisObj.decoration) + '\n'; var body = line; body += thisObj.decoration + ' ' + message + ' ' + thisObj.decoration + '\n'; body += line; return body; }
これを次のように書くとうまくいきません。createメソッドからcreateBodyメソッドを呼んだときの this.decorationはundefinedになります。JavaScriptのthisの難しいところですね。
function create(to, message){ var head = createHead(to); var body = createBody(message); var contents = head + body; this.history.push(contents); return contents; } function createBody(message){ //ここでのthisはこのオブジェクトを参照していない var line = createLine(message.length + 4, this.decoration) + '\n'; var body = line; body += this.decoration + ' ' + message + ' ' + this.decoration + '\n'; body += line; return body; }
まとめ
Revealing Prototype Patternはプロトタイプを使って実行上の効率が良くなるものの、thisの参照に気を払わなければならない箇所があり、気をつけないと実装ミスにつながりそうだなと感じました。(JavaScriptに慣れていればそんなことはないのかもしれませんね・・・。)
一方、Revealing Module Patternの方が直観に反せずコーディングできそうです。なので、オブジェクトを山のように作る事がなければRevealing Module Patternでもいいのではないでしょうか。例えばTodoアプリを作るとして、1画面に1000個のTodoを表示させることはないと思います。複数ユーザの処理をさばくサーバーサイドはともかく、クライアントサイドに関してはRevealing Prototype Patternにしようが、Revealing Module Patternにしようが、さほどパフォーマンス的に気になるレベルではないと思いますし、好みの方を選択すればいいのかなと思いました。