このような継承ヘルパーで私が見る問題は
継承されたクラスは、引数なしを使用new
して呼び出すことで作成されます。
これは、コンストラクターが特定の引数に依存している場合、継承プロセス中に、 (私の目には) もう少し複雑なコンストラクターを簡単に壊す可能性があります。constructor
subClass.prototype=new F();
それで、それを回避し、あなたの質問が何であるかを実行できるようにするために、私は現在このようなものを使用しています:
申し訳ありませんが、最初の質問を誤解し、拡張と縮小をサポートするために少し使用したヘルパーを変更しました。
var base = (function baseConstructor() {
var obj = {
create: function instantiation(extend) {
var instance, args = [].slice.call(arguments);
if (this === base) {
throw new SyntaxError("You can't create instances of base");
} else if (!this.hasOwnProperty("initclosure")) {
throw new SyntaxError("Cannot create instances without an constructor");
} else if (this.singleton && this.instances.length !== 0) {
throw new SyntaxError("You can't create more than one Instance of a Singleton Class");
} else {
if (!extend._class || !extend._class.isPrototypeOf(this)) {
instance = Object.create(this.pub);
this.instances.push(instance);
} else {
args = args.slice(1);
extend._class.remove(extend);
instance = this.extend(extend);
}
this.init.apply(instance, args);
instance.onCreate();
return instance;
}
},
extend: function (instance) {
if (!instance._class.isPrototypeOf(this)) {
return;
}
var extended = Object.create(this.pub);
for (var propInst in instance) {
if (instance.hasOwnProperty(propInst)) {
extended[propInst] = instance[propInst];
}
}
instance._class.remove(instance);
this.instances.push(extended);
return extended;
},
reduce: function (instance) {
if (!instance.instanceOf(this)) {
return;
}
var reduced = Object.create(this.pub);
for (var propRed in instance) {
if (instance.hasOwnProperty(propRed)) {
reduced[propRed] = instance[propRed];
}
}
instance._class.remove(instance);
this.instances.push(reduced);
return reduced;
},
remove: function (instance) {
if (instance.instanceOf(this)) {
var removed = this.instances.splice(this.instances.indexOf(instance), 1)[0];
instance.onRemove();
return removed;
}
},
inherit: function inheritation(specsOpt) {
specsOpt = specsOpt || {};
applyDefaults(specsOpt, {
singleton: false,
anonymous: false
});
var sub = Object.create(this);
sub.pub = Object.create(this.pub);
sub.pub.proto = this.pub;
sub.pub._class = sub;
sub.instances = [];
sub.anonymous = specsOpt.anonymous;
sub.sup = this;
if (specsOpt.singleton) {
sub.singleton = specsOpt.singleton;
sub.getSingleton = getSingleton;
protect.call(sub, {
singleton: {
writable: false,
configurable: false,
enumerable: false
},
getSingleton: {
writable: false,
configurable: false
}
});
}
return sub;
},
initclosure: function Base() {},
instances: [],
pub: {
instanceOf: function (obj) {
if (!obj || !obj.pub) {
return this.className;
}
return obj.pub.isPrototypeOf(this);
},
onRemove: function () {},
onCreate: function () {},
"_class": obj
}
};
/* Helper Functions. --- Use function expressions instead of declarations to get JSHint/Lint strict mode violations
*
* TODO: Maybe add an obj.helper Propertie with usefull functions
*/
var applyDefaults = function (target, obj) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
target[prop] = target[prop] || obj[prop];
}
}
};
var getSingleton = function () { //To get past the strict violation
return this.instances[0];
};
var protect = function (props, desc) { //Maybe change it a little
for (var prop in props) {
if (props.hasOwnProperty) {
Object.defineProperty(this, prop, props[prop] || desc);
}
}
return this;
};
/* End Helpers
*
* Protecting
*/
Object.defineProperty(obj, "init", {
set: function (fn) {
if (typeof fn !== "function") {
throw new Error("Expected typeof init to be 'function'");
} else if (Boolean(fn.name) === this.anonymous) {
try {
throw new Error("Expected the constructor " + (!this.anonymous ? "not " : "") + "to be Anonymous");
} catch (e) {
console.error(e.stack);
}
}
if (!this.hasOwnProperty("initclosure")) {
this.initclosure = fn;
this.pub.constructor = this.init;
this.pub.className = fn.name;
protect.call(this.pub, {
constructor: false,
className: false
}, {
enumerable: false
});
}
},
get: function () {
var that = this;
var init = function init() {
if (that.pub.isPrototypeOf(this)) {
that.initclosure.apply(this, arguments);
} else {
throw new Error("init can't be called directly");
}
};
init.toString = function () {
return that.initclosure.toString();
};
return init;
}
});
obj.toString = function () {
return "[class " + (this.initclosure.name || "Class") + "]";
};
obj.pub.toString = function () {
return "[instance " + (this.className || "Anonymous") + "]";
};
protect.call(obj, {
create: false,
inherit: false,
toString: false,
onRemove: {
enumerable: false
},
onCreate: {
enumerable: false
},
initclosure: {
enumerable: false
}
}, {
writable: false,
configurable: false
});
protect.call(obj.pub, {
instanceOf: false,
toString: false,
"_class": {
enumerable: false
}
}, {
writable: false,
configurable: false,
enumerable: false
});
return obj;
})();
Object.create
注:これはECMAScript 5で導入されたものに依存しているため、古いブラウザーではサポートされていません
継承ヘルパーが与えられた場合、いくつかの「クラス」を作成できます
var Car = base.inherit();
Car.pub.getTopSpeed = function () {
return 100;
};
Car.init = function ClassCar(model) {
this.model = model || this.model || "";
};
Car.pub.type = "car";
継承可能なスーパークラスができたのでTruck
、Car
var Truck = Car.inherit();
Truck.pub.getTopSpeed = function () {
return 20;
};
Truck.pub.type = "truck";
Truck.init = function ClassTruck(model, color) {
Truck.sup.init.apply(this, [].slice.call(arguments));
this.color = color;
};
次に、のインスタンスを作成しましょうCar
var myCar = Car.create("Porsche");
console.log(myCar.getTopSpeed(), myCar.className); //100, ClassCar
正しく理解できたら、の既存のインスタンスを拡張してのインスタンスmyCar
にCar
しますTruck
。
もしそうなら、これをしましょう
var myExtendedTruck = Truck.extend(myCar);
console.log(myExtendedTruck.getTopSpeed(), myExtendedTruck.className); //20, ClassTruck
console.log(myExtendedTruck.instanceOf(Truck)); //true
これは、拡張されたプロトタイプチェーンを設定するだけで、インスタンス変数を新しいTruckインスタンスにコピーします。つまり、Car
インスタンスはTruck
インスタンスになります
または、コンストラクターも使用する場合。
create
スーパークラスのインスタンスを渡すときにも機能します。
次に、拡張されて初期化されます。
var myConstructedExtendedTruck = Truck.create(myCar, myCar.model, "Yellow");
console.log(myConstructedExtendedTruck.getTopSpeed(), myConstructedExtendedTruck.model, myConstructedExtendedTruck.color); //20 , Porsche , Yellow
これで、extendCar
インスタンスができました。これは、コンストラクターのインスタンスでTruck
あり、コンストラクターによって適切に構築されていTrucks
ます。
これが正しければ、スーパークラスのインスタンスにも戻れるようになります。
var myReducedCar = Car.reduce(myExtendedTruck);
console.log(myReducedCar.getTopSpeed(), myReducedCar.className); //100, ClassCar
console.log(myReducedCar.instanceOf(Truck)); //false
少しいじるJSBinの例を次に示します
編集メモ:Classesinstances
配列内のインスタンスを適切に移動するようにコードを修正しました