我在阅读http://javascript.crockford.com/prototypal.html之后一直在玩原型继承,并且在理解如何以我使用经典继承的方式使用它时遇到了一些问题。也就是说,原型继承的所有函数和变量基本上都是静态的,除非它们被子对象覆盖。请考虑以下代码段:
var Depot = {
stockpile : [],
loadAmmo : function (ammoType) {
this.stockpile.push(ammoType);
}
};
var MissileDepot = Object.create(Depot);
var GunDepot = Object.create(Depot);
肯定应该在原型中使用 stockpile
和loadAmmo
,因为MissileDepot和GunDepot都有它们。然后我们运行:
MissileDepot.loadAmmo("ICBM");
MissileDepot.loadAmmo("Photon Torpedo");
alert(MissileDepot.stockpile); // outputs "ICBM,Photon Torpedo"
alert(GunDepot.stockpile); // outputs "ICBM,Photon Torpedo"
这是预料之中的原因,因为MissileDepot
和GunDepot
在其对象中实际上都有stockpile
或loadAmmo
,因此javascript会将继承链查找到其共同的祖先。
当然我可以手动设置GunDepot的库存,正如预期的那样,解释器不再需要查找链
GunDepot.stockpile = ["Super Nailgun", "Boomstick"];
alert(GunDepot.stockpile); // outputs "Super Nailgun,Boomstick"
但这不是我想要的。如果这是经典继承(比如Java),loadAmmo将作为实例方法和实例变量独立地对MissileDepot和GunDepot的库存进行操作。 我希望我的原型能够声明孩子们常见的东西,而不是他们分享的东西。
所以也许我完全误解了原型继承背后的设计原则,但我不知道如何实现我刚刚描述的内容。有小费吗?提前谢谢!
答案 0 :(得分:8)
Javascript提供了一种方法,以U的方式来做到这一点:) 试试这个:
function Depot() {
this.stockpile = [],
this.loadAmmo = function (ammoType) {
this.stockpile.push(ammoType);
}
};
var MissileDepot = new Depot();
var GunDepot = new Depot();
MissileDepot.loadAmmo("ICBM");
MissileDepot.loadAmmo("Photon Torpedo");
alert(MissileDepot.stockpile); // outputs "ICBM,Photon Torpedo"
alert(GunDepot.stockpile); // outputs ""
之后你可以随时添加这些功能:
MissileDepot.blow = function(){alert('kaboom');}
使用另一个对象扩展对象也是一种选择,但你想要的是,javascript中的OO编程是由函数完成的,而不是{};}的对象
编辑:
我不好意思写这篇文章而不提及:javascript“new”关键字只是为了让OO老兵更容易。请深入了解原型继承和动态对象创建,因为其中真正的魔力! :)
答案 1 :(得分:3)
对于该方法,所有方法都按预期工作。这只是你需要照顾的领域。
我在YUI中看到很多,是构造函数分配实例varialbes。从父级继承的“类”调用其父级的构造函数。看这里: http://developer.yahoo.com/yui/docs/DataSource.js.html
示例基类:
util.DataSourceBase = function(oLiveData, oConfigs) {
...
this.liveData = oLiveData;
... more initialization...
}
示例子类:
util.FunctionDataSource = function(oLiveData, oConfigs) {
this.dataType = DS.TYPE_JSFUNCTION;
oLiveData = oLiveData || function() {};
util.FunctionDataSource.superclass.constructor.call(this, oLiveData, oConfigs);
};
// FunctionDataSource extends DataSourceBase
lang.extend(util.FunctionDataSource, util.DataSourceBase, {
...prototype of the subclass...
});
答案 2 :(得分:1)
要实现您的目标,您需要一种克隆方法。你不想要一个继承原型,你想要一个克隆原型。看一下已经实现的Object.clone()函数之一,比如prototypejs的一个:http://api.prototypejs.org/language/object.html#clone-class_method
如果您想坚持某种原型设计,则必须实现一个initialize()方法,该方法将为您新创建的Depot提供 stockpile 属性。这就是prototypejs类的定义方式:克隆原型和initialize()方法:http://prototypejs.org/learn/class-inheritance
答案 3 :(得分:1)
John Resig写了一个small script来模拟经典继承。你可能想检查一下。
答案 4 :(得分:0)
JavaScript中实例变量的秘密在于它们在超类中定义的方法或包含的模块中共享。语言本身不提供这样的功能,并且可能无法与Prototypal继承进行交互,因为每个实例都需要它自己的实例变量包,但是通过使用规则和约定,它实现起来相当简单。
// Class Depot
function Depot(I) {
// JavaScript instance variables
I = I || {};
// Initialize default values
Object.reverseMerge(I, {
stockpile: []
});
return {
// Public loadAmmo method
loadAmmo: function(ammoType) {
I.stockpile.push(ammoType);
},
// Public getter for stockpile
stockpile: function() {
return I.stockpile;
}
};
}
// Create a couple of Depot instances
var missileDepot = Depot();
var gunDepot = Depot();
missileDepot.loadAmmo("ICBM");
missileDepot.loadAmmo("Photon Torpedo");
alert(missileDepot.stockpile()); // outputs "ICBM,Photon Torpedo"
alert(gunDepot.stockpile()); // outputs ""
// Class NonWeaponDepot
function NonWeaponDepot(I) {
I = I || {};
// Private method
function nonWeapon(ammoType) {
// returns true or false based on ammoType
}
// Make NonWeaponDepot a subclass of Depot and inherit it's methods
// Note how we pass in `I` to have shared instance variables
return Object.extend(Depot(I), {
loadAmmo: function(ammoType) {
if(nonWeapon(ammoType)) {
// Here I.stockpile is the same reference an in the Depot superclass
I.stockpile.push(ammoType);
}
}
});
}
var nonWeaponDepot = NonWeaponDepot();
nonWeaponDepot.loadAmmo("Nuclear Bombs");
alert(nonWeaponDepot.stockpile()); // outputs ""
这就是如何在JavaScript中执行实例变量。 Another instance variable example using the same technique