【问题标题】:JavaScript property inheritanceJavaScript 属性继承
【发布时间】:2012-08-15 04:05:46
【问题描述】:

我正在尝试创建一个通用的“列表”类,它将具有:

  • 属性:Items - 这将是一个“what-ever”数组
  • 方法:Add() - 这将是抽象的并由特定的“列表”对象实现
  • 方法:Count() - 返回“项目”的数量

然后创建继承自 'List' 的子类:

// Class 'List'
function List(){
    this.Items = new Array();
    this.Add = function(){ alert('please implement in object') }
}

// Class CDList - which inherits from 'List'
function CDList(){
    this.Add = function(Artist){
        this.Items.push(Artist)
    }
}
CDList.prototype = new List();
CDList.prototype.constructor = CDList;

// Create a new CDList object
var myDiscs = new CDList();
myDiscs.Add('Jackson');
myDiscs.Count()  <-- this should be 1


// Create a second CDList object
var myDiscs2 = new CDList();
myDiscs2.Add('Walt');
myDiscs2.Add('Disney');
myDiscs2.Count()  <-- this should be 2

...但这似乎为所有“CDList”实例创建了一个共享的“项目”列表。我需要以某种方式为每个“CDList”实例创建一个“Items”列表的新继承实例。

我该怎么做?

*我在本例中使用“项目”列表作为示例。我希望能够在我的子类中为任何类型的继承属性添加一个新实例 - 不一定是 Array 对象。

【问题讨论】:

标签: javascript inheritance properties


【解决方案1】:

试试这个:

function CDList(){
    List.call( this )
    this.Add = function(Artist){
        this.Items.push(Artist)
    }
}

你需要调用超级构造函数...

我喜欢this article of the MDN network about JavaScript inheritance。我尝试了这种方法/技术,它在我测试的所有浏览器(Chrome、Safari、Internet Explorer 8+ 和 Firefox)中都运行良好。

【讨论】:

  • 非常感谢!我也会看看 MDN 上的那篇文章。
  • 这是我发现的最直接的继承方法。您只需要记住将所有方法设置为构造函数中的属性,而不是原型中。
【解决方案2】:

只有一个数组,因为您只创建一个。此数组附加到“CDList”的原型,因此在所有实例之间共享。

解决这个问题:不要把它附加到原型上,而是附加到实例上。这只能在构建时完成:

// This is the constructor of the parent class!
function List() {
    this.Items = new Array();
}

// Add methods to the prototype, not to the instance ("this")
List.prototype.Add = function() { alert('please implement in object'); };

// Constructor of the child
function CDList() {
    List.call(this); // <-- "super();" equivalent = call the parent constructor
}

// "extends" equivalent = Set up the prototype chain
// Create a new, temporary function that has no other purpose than to create a
// new object which can be used as the prototype for "CDList". You don't want to
// call "new List();", because List is the constructor and should be called on
// construction time only. Linking the prototypes directly does not work either,
// since this would mean that overwriting a method in a child overwrites the
// method in the parents prototype = in all child classes.
var ctor = function() {};
ctor.prototype = List.prototype;
CDList.prototype = new ctor();
CDList.prototype.constructor = CDList;

// Overwrite actions
CDList.prototype.Add = function(Artist) {
    this.Items.push(Artist);
};

演示:http://jsfiddle.net/9xY2Y/1/


一般概念是:每个实例必须有自己的副本(如本例中的“Items”数组)必须在构造时创建并附加到“this”(= 实例),即在执行时new List()new CDList()。可以跨实例共享的所有内容都可以附加到原型上。这实质上意味着像“添加”函数这样的属性只被创建一次,然后被所有实例使用(导致原始问题的原因)。

链接原型时,不能直接链接它们(通常),例如:

CDList.prototype = List.prototype;
DVDList.prototype = List.prototype;

// Now add a new function to "CDList"
CDList.prototype.Foo = function() { alert('Hi'); };

由于“List”、“CDList”和“DVDList”三个函数的原型是直接相连的,它们都指向一个原型对象,即List.prototype。所以,如果你添加一些东西到CDList.prototype 你实际上是把它添加到List.prototype - 这也是“DVDList”的原型。

var dvd = new DVDList();
dvd.Foo(); // <-- alerts "hi" (oops, that wasn't intended...)

诀窍是将原型链接到父类的新实例:

CDList.prototype = new List();

这将创建一个“List()”类型的新对象,其特殊功能是“List()”函数的原型链接到新对象,使您能够直接在对象上调用原型的属性:

var l = new List();
alert( l.hasOwnProperty("Add") );  // <-- yields "false" - the object l has no
                                   // property "Add"
l.Add("foo"); // <-- works, because the prototype of "List" has a property "Add"

但是,请记住,我们打算使用函数“List()”的主体在每个实例的基础上创建像这个数组“Items”这样的东西?这是您放置任何“构造函数”代码的地方,例如

function User(userId) {
    $.getJSON('/user/' + userId, ...
}

function Admin() {}
Admin.prototype = new User( // ... now what?

一个非常干净的解决方案是使用另一个函数来创建原型对象:

var ctor = function() {}; // <-- does nothing, so its super safe
                          // to do "new ctor();"

现在可以直接链接原型了,因为我们永远不会向ctor.prototype添加任何东西:

ctor.prototype = List.prototype;

如果我们这样做:

CDList.prototype = new ctor();

“CDList()”的原型变成了“ctor”类型的新对象,它没有自己的属性但可以扩展,例如通过新的“添加”功能:

CDList.prototype.Add = function() { /* CD specific code! */ };

然而,如果你不给这个新的原型对象添加一个“Add”属性,“ctor()”的原型就会起作用——它是“List()”的原型。这就是期望的行为。

此外,"List()" 中的代码现在仅在您执行 new List() 或直接从另一个函数(在子类中通过 List.call(this);)调用它时执行。

【讨论】:

  • 感谢您的详尽解释以及 jsfiddle 示例!非常感谢队友!
  • 能否请您提供一些关于您在此处所写内容的更多信息:“您不想调用 'new List();',因为 List 是构造函数,应该调用仅在施工时间。”为什么会这样?
  • @user1566994 当然,我在回答中添加了一些关于一般程序的信息。我不太确定这是否可以理解,所以请随时提出任何问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-03
  • 2020-01-25
  • 2013-09-04
  • 2013-03-05
  • 1970-01-01
相关资源
最近更新 更多