prototypeとObject

ってツイートしたら「後者は問題ありそう」みたいな反応があって、色々調べてみました。

見通しの良さ

元々は以下のfooとhogeを比べた時、
「Objectだけで書いたほうがタイプ数少ないし見通し良いよ」
って友達に言われたのがキッカケでした。

var foo = function (name) {
	this.name = (name) ? name : 'foo';
};
foo.prototype.method = function () {
	console.log(this.name);
};

var hoge = {
	name: 'hoge',
	method: function () {
		console.log(this.name)
	}
};

newの代わりとしてのObject.create()

じゃあそれぞれインスタンスの生成やるとどうなるか

var bar = new foo('bar');
bar.method(); // =>bar

var fuga = Object.create(hoge, {name: {value: 'fuga'}});
fuga.method(); // => fuga

これでそのまま使える気がしてきますが、ちょっと問題があります。

問題1:書き換え不可

既定でプロパティは
書き換え (writable)、列挙 (enumerable)、変更 (configurable)
が不可になります:

これ、どういうことかというと

var bar = new foo('bar');
bar.method(); // => bar
bar.name = 'barbar';
bar.method(); // => barbar

var fuga = Object.create(hoge, {name: {value: 'fuga'}});
fuga.method(); // => fuga
fuga.name = 'fugafuga'; // ここで書き換えても
fuga.method(); // => fuga 値そのまま


これの制限を解除するには第二引数でオプションを加えなきゃいけません。

var piyo = Object.create(hoge, {
	name: {
		value: 'piyo',
		writable: true,
		enumerable: true,
		configurable: true
	}
});
piyo.method(); // => piyo
piyo.name = 'piyopiyo';
piyo.method(); // => piyopiyo

これでは「Objectだけで書いたほうがタイプ数少ない」が本末転倒です。

問題2:初期化不可

また、Object.createでは以下の様な初期化コードの実行が出来ません

var foo = function () {
	console.log('Hello:D');
};

var bar = new foo(); // => Hello :D

解決策

以上の2つの問題を解決するのに、init関数を作成する運び

var baz = {
	init: function (name) {
		this.name = name;
		console.log('Hello:D');
	},
	method: function (){
		console.log(this.name);
	}
};
var qux = Object.create(baz);
qux.method(); // => undefined
qux.init('qux'); // => Hello :D
qux.method(); // => qux
qux.name = 'quxqux';
qux.method(); // => quxqux

これでスッキリしました。

Polyfill

あとはECMAScript 5thの仕様からして古いブラウザには対応していないので
Polyfillとして

if (!Object.create) {
    Object.create = function (o) {
        if (arguments.length > 1) {
            throw new Error('Object.create implementation only accepts the first parameter.');
        }
        function F() {}
        F.prototype = o;
        return new F();
    };
}

とかやると良いかもしれません。

まとめ

複数のインスタンスを生成するとか発想なしに、
とりあえずでObjectだけで組まれたコードを見かけたことがあるので
こうやって書き換えればうまく保守できるなーとか思いました。


ちなみに、個人的にはnewの方が好きです。
ネストが深くならないしー かっこいいしー


参考
https://developer.mozilla.org/ja/docs/JavaScript/Reference/Global_Objects/Object/create


おしまい。



追記
ご丁寧なコメント頂きました(*´∀`*)

 gocho
「prototypeを用いた記述」と「オブジェクトメンバーとしての記述」は記述だけでなく、挙動も異なります.
(動作確認)
http://jsfiddle.net/gocho/d3RmZ/
(詳しい解説)
http://d.hatena.ne.jp/maeharin/20130215/javascript_prototype_chain



おしまい

開眼!  JavaScript ―言語仕様から学ぶJavaScriptの本質

開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質