JavaScriptでnew演算子をつけてもつけなくても同じようにインスタンスを作成の続き

またJavaScriptで遊んでる。前回→JavaScriptでnew演算子をつけてもつけなくても同じようにインスタンスを作成 - yuyakkoの日記

初期化の部分を別の関数に分けておけばコピペせずにコンストラクタを使い回せそうだったので試す。
最初は失敗。

function SafeConstructor(){
	var callee = arguments.callee;
	if(this instanceof callee){
		this.init.apply(this, arguments);
	}else{
		var EmptyConstructor = function(){}
		EmptyConstructor.prototype = callee.prototype;
		var obj = new EmptyConstructor;
		obj.init.apply(obj, arguments);
		return obj;
	}
}

var Class1 = SafeConstructor;
Class1.prototype.init = function(){
	this.arg = Array.apply(this, arguments);
};
Class1.prototype.toString = function(){
	return "Class1/"+this.arg.toString();
}

var Class2 = SafeConstructor;
Class2.prototype.init = function(){
	this.arg = Array.apply(this, arguments);
};
Class2.prototype.toString = function(){
	return "Class2/"+this.arg.toString();
}

// 使ってみる
var instance1 = new Class1(1, 2);
var instance2 = Class2(3, 4, 5);
alert("instance1="+instance1+"\n"
	+"Class1のインスタンス"+(instance1 instanceof Class1 ? "です" : "ではありません")+"\n"
	+"Class2のインスタンス"+(instance1 instanceof Class2 ? "です" : "ではありません")+"\n"
	+"\n"
	+"instance2="+instance2+"\n"
	+"Class1のインスタンス"+(instance2 instanceof Class1 ? "です" : "ではありません")+"\n"
	+"Class2のインスタンス"+(instance2 instanceof Class2 ? "です" : "ではありません"));


prototypeを共有してしまってる。


コンストラクタ作成関数を作って毎回違うオブジェクトにすればよさそう。

function makeSafeConstructor(){
	return function(){
		var callee = arguments.callee;
		if(this instanceof callee){
			this.init.apply(this, arguments);
		}else{
			var EmptyConstructor = function(){}
			EmptyConstructor.prototype = callee.prototype;
			var obj = new EmptyConstructor;
			obj.init.apply(obj, arguments);
			return obj;
		}
	};
}
// 一部省略
var Class1 = makeSafeConstructor();
var Class2 = makeSafeConstructor();


よし。


"init"っていう名前を与えなくても無名関数でいいんじゃないかな。
初期化関数は引数で受け取るように変えてみる。

function makeSafeConstructor(init){
	var SafeConstructor = function(){
		if(this instanceof arguments.callee){
			init.apply(this, arguments);
		}else{
			var EmptyConstructor = function(){}
			EmptyConstructor.prototype = arguments.callee.prototype;
			var obj = new EmptyConstructor;
			init.apply(obj, arguments);
			return obj;
		}
	}
	return SafeConstructor;
}
// いろいろ省略
var Class1 = makeSafeConstructor(function(){
	this.arg = Array.apply(this, arguments);
});

初期化関数なんてケチなこと言わずにコンストラクタを受け取るようにしてもいいかも。

function makeSafeConstructor(Constructor){
	var SafeConstructor = function(){
		if(this instanceof arguments.callee){
			return Constructor.apply(this, arguments);
		}else{
			var EmptyConstructor = function(){}
			EmptyConstructor.prototype = arguments.callee.prototype;
			var obj = new EmptyConstructor;
			var result = Constructor.apply(obj, arguments);
			if(result instanceof Object){
				return result;
			}
			return obj;
		}
	}
	SafeConstructor.prototype = Constructor.prototype;
	return SafeConstructor;
}

// makeSafeConstructor使わずにClass1作って…
function Class1(){
	this.arg = Array.apply(this, arguments);
}
Class1.prototype.toString = function(){
	return "Class1/"+this.arg.toString();
}
// 後から入れ替え
Class1 = makeSafeConstructor(Class1);

// 最初から使うように書いてもOK?
var Class2 = makeSafeConstructor(function(){
	this.arg = Array.apply(this, arguments);
});
Class2.prototype.toString = function(){
	return "Class2/"+this.arg.toString();
}

// 使ってみるところは省略

参考

前回忘れていた一番重要な資料