继承变量属性

时间:2013-03-17 13:41:57

标签: javascript inheritance

来自codeacademy的练习:

function Penguin(name) {
    this.name = "Pingy";
    this.numLegs = 2;
}

// create your Emperor class here and make it inherit from Penguin
function Emperor (name){
    this.name = name;
}
Emperor.prototype = new Penguin();
var emp = new Emperor("Empy");

所以我让Emperor继承了Penguin的属性,现在我知道emp.numLegs将是2。

阅读评论后,我编辑了这个问题: 如你所见,我在创建emp时给出了一个名字,而emp.name确实是我的新emp的名字,即“Empy”。但是从Penguin类构造函数继承的name呢?它去哪儿了?

4 个答案:

答案 0 :(得分:8)

Codecademy是初学者学习JavaScript的好方法。学习任何编程语言都需要练习,Codecademy让你练习。

但有时你需要超越实践的范围并学习一些理论。阅读以下答案。它很好地解释了JavaScript中的原型继承:https://stackoverflow.com/a/8096017/783743

现在你有一个Penguin构造函数,如下所示:

function Penguin(name) {
    this.name = name;
    this.numLegs = 2;
}

然后,您创建一个Emperor构造函数,该构造函数继承自Penguin

function Emperor(name) {
    this.name = name;
}

Emperor.prototype = new Penguin();

请注意,您正在创建Penguin的实例,并将其分配给Emperor.prototype。因此:

  1. Emperor.prototype.name = undefined - 这是因为您没有将任何name传递给Penguin构造函数。如果我写了Emperor.prototype = new Penguin("Empy"),那么Emperor.prototype.name就是"Empy"
  2. Emperor.prototype.numLegs = 2 - 显然。
  3. 现在,当您按如下所示创建新的Emperor时,您需要为构造函数指定一个名称(请记住,您从undefined继承了Penguin作为名称)。因此:

    var emp = new Emperor("Empy");
    

    这是旧学校的方法。

    现在JavaScript程序员使用Object.createcall继承另一个构造函数。让我用一个例子来解释:

    function Emperor(name) {
        Penguin.call(this, name);
    }
    
    Emperor.prototype = Object.create(Penguin.prototype);
    Emperor.prototype.constructor = Emperor;
    

    这种方法比旧学校方法有几个优点:

    1. 我们使用Penguin继承new Penguin()的原型成员,而不是使用Penguin创建Object.create(Penguin.prototype)的新实例并且不向其传递任何参数。这也可以防止在实际调用派生构造函数之前对基础构造函数进行不必要的初始化。
    2. 我们不再在派生的构造函数中编写this.name = name,而是使用Penguin.call(this, name)为我们调用基础构造函数。这种模式称为mixin,如果基本构造函数需要在运行时初始化或需要维护自己的状态信息,则非常有用。
    3. 请注意,我们还添加了一个附加声明Emperor.prototype.constructor = Emperor。这是因为prototype是一个非常特殊的属性,存在于所有函数中。它指向一个具有非常特殊的constructor属性的对象,该属性指向函数本身。通过将Emperor.prototype设置为其他内容,我们会失去此属性。因此我们再次设置它。

      这两个例子的净效果是相同的。但是对于更复杂的代码,使用新方法要好得多。快乐学习JavaScript。

答案 1 :(得分:5)

我假设您想知道为什么必须在构造函数中为nameEmperor提供this.name = name;作为参数。

JavaScript中的原型继承非常简单,没有隐藏的魔法。您在EmperorPenguin之间提供的唯一连接就在此行中:

Emperor.prototype = new Penguin();

Emperor.prototype现在是Penguin的一个实例,但就此而言,它可能是任何对象函数 Emperor对函数Penguin一无所知,因此它不会神奇地调用Penguin

当您使用Func调用函数(new)时,它所做的只是创建一个新的空对象,其原型是函数prototype属性(Func.prototype) 。此对象将成为构造函数中的this值,如果没有返回其他对象,将隐式返回。

  

但是从Penguin类构造函数继承的名称怎么样?它去哪儿了?

它是Emperor.prototype的属性,并被每个Emperor实例的name属性遮蔽。

看看my answer here我在哪里创建了一些很好的ASCII图表来解释实例与它们的原型之间的关系。


注意: Emperor.prototype = new Penguin();实际上不是建立继承的好方法。如果Penguin 需要参数,会发生什么?什么会通过?

此时您实际上不想创建Penguin的新实例,您只想将Penguin的{​​{1}}连接到原型链中。您可以使用prototype轻松完成此操作:

Object.create

但是新的Emperor.prototype = Object.create(Penguin.prototype); Emperor.prototype.constructor = Emperor; 对象不再具有Emperor属性。这就是为什么你必须在每个新的numLegs实例上调用Penguin,就像使用Emperor的其他语言一样:

super()

答案 2 :(得分:1)

调用new Penguin()时,此方法将创建一个对象并返回该对象的原型。

在进行原型保留时,您唯一要做的就是首先创建一个新的Penguin对象,然后将这些值放在Emperor的原型中。现在,当您创建新的Emperor对象时,原型已设置为Penguin的值。要覆盖它们,您需要在新的构造函数中再次设置它们。

JavaScript中的Inhertiance与其他语言非常不同。

代码示例:

function Penguin(name) {
    this.name = name;
    this.legs = 2;
}

function Emperor(name) {
    this.name = name;
}
Emperor.prototype = new Penguin();
/**
 * The prototype of Emperor now contains:
 * 
       {
           "name": undefined,
           "legs": 2
       }
 * 
 * Because you didn't add an argument to the constructor of `Penguin`
 */

var e = new Emperor('foo');
// The name property will get overriden with 'foo'

答案 3 :(得分:1)

创建Penguin对象时,还需要将名称传递给name参数。因此,当您创建类型为Penguin的新对象时,您必须传递名称。类似地,因为Emperor继承自Penguin对象,您必须在构造函数中传递值,这样如果您希望执行与基类Penguin中发生的处理不同的处理,则可以在子类构造函数中定义它。但是Emperor可以是任何类型的对象对象,因为它没有关于Penguin定义的信息。

如果在“Emperor”类的定义中删除了行

 this.name = name;

然后你可以在构造函数中传递任何变量,仍然得到超级构造函数中定义的值“Pingy”

如果添加行

 Penguin.call(this, name) ;// As to call super class instance

然后,您将值正确传递到新对象上,同时numLegs