在原型继承中实现实例方法/变量

时间:2010-01-21 08:18:32

标签: javascript

我在阅读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);
肯定应该在原型中使用

stockpileloadAmmo,因为MissileDepot和GunDepot都有它们。然后我们运行:

MissileDepot.loadAmmo("ICBM");
MissileDepot.loadAmmo("Photon Torpedo");

alert(MissileDepot.stockpile); // outputs "ICBM,Photon Torpedo"
alert(GunDepot.stockpile); // outputs "ICBM,Photon Torpedo"

这是预料之中的原因,因为MissileDepotGunDepot在其对象中实际上都有stockpileloadAmmo,因此javascript会将继承链查找到其共同的祖先。

当然我可以手动设置GunDepot的库存,正如预期的那样,解释器不再需要查找链

GunDepot.stockpile = ["Super Nailgun", "Boomstick"];
alert(GunDepot.stockpile); // outputs "Super Nailgun,Boomstick"

但这不是我想要的。如果这是经典继承(比如Java),loadAmmo将作为实例方法和实例变量独立地对MissileDepot和GunDepot的库存进行操作。 我希望我的原型能够声明孩子们常见的东西,而不是他们分享的东西。

所以也许我完全误解了原型继承背后的设计原则,但我不知道如何实现我刚刚描述的内容。有小费吗?提前谢谢!

5 个答案:

答案 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