Javascript 变量属性的继承性

Javascript 变量属性的继承性,javascript,inheritance,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 em

来自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");
所以我让
emper
继承
Penguin
的属性,现在我知道
emp.numLegs
将是2

阅读评论后,我编辑了问题:
正如您所见,我在创建
emp
时给出了一个名称,
emp.name
将确实是我的新emp的名称,即“Empy”。但是继承自企鹅类构造函数的
名称如何?它到哪里去了?

我想您想知道为什么必须提供
name
作为
emperon
this.name=name的参数在构造函数内部

JavaScript中的原型继承非常简单,没有隐藏的魔力。您在
emperon
Penguin
之间提供的唯一连接如下:

Emperor.prototype = new Penguin();
Emperor.prototype
现在是
Penguin
的一个实例,但它可以是任何对象。函数
emperon
对函数
Penguin
一无所知,因此它不会神奇地调用
Penguin

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

但是从企鹅类构造函数继承的名称呢?它去哪里了

它是
emperon.prototype
的一个属性,并且被每个
emperon
实例的
name
属性隐藏

看看我在哪里创建了一些漂亮的ASCII图表,解释了实例与其原型之间的关系


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

此时您实际上不想创建一个新的
Penguin
实例,您只想将
Penguin
prototype
连接到prototype链中。使用
对象可以轻松完成此操作。创建

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


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

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

JavaScript中的固有性与其他语言非常不同

代码示例:

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'

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

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

 this.name = name;
然后,您可以在构造函数中传递任何变量,仍然可以获得超级构造函数中定义的值“Pingy”

如果你加上一行

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

然后使用
numLegs
as 2将值正确地传递到新对象上,Codecademy也是初学者学习JavaScript的好方法。学习任何编程语言都需要练习,Codecademy让你练习

然而,有时你需要超越实践的范围,学习一些理论。阅读下面的答案。它很好地解释了JavaScript中的原型继承:

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

function Penguin(name) {
    this.name = name;
    this.numLegs = 2;
}
然后创建一个继承自企鹅的
emperon
构造函数:

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

Emperor.prototype = new Penguin();
请注意,您正在创建一个
Penguin
的实例,并将其分配给
emperon.prototype
。因此:

  • employer.prototype.name=undefined
    -这是因为您没有向
    Penguin
    构造函数传递任何
    name
    。如果我写的是
    emper.prototype=new Penguin(“Empy”)
    ,那么
    emper.prototype.name
    将是
    “Empy”
  • employer.prototype.numLegs=2
    -显然
  • 现在,当您按照如下方式创建一个新的
    emperon
    时,您需要给构造函数一个名称(请记住,您是从
    Penguin
    继承
    undefined
    作为名称)。因此:

    那是老派的方法。

    现在JavaScript程序员使用
    Object.create
    call
    从另一个构造函数继承。让我举例说明:

    function Emperor(name) {
        Penguin.call(this, name);
    }
    
    Emperor.prototype = Object.create(Penguin.prototype);
    Emperor.prototype.constructor = Emperor;
    
    与传统方法相比,这种方法有几个优点:

  • 我们没有使用
    new Penguin()
    创建
    Penguin
    的新实例,也没有向其传递任何参数,而是使用
    Object.create(Penguin.prototype)
    继承
    Penguin
    的原型成员。这还可以防止在deriv之前对基本构造函数进行不必要的初始化
    var emp = new Emperor("Empy");
    
    function Emperor(name) {
        Penguin.call(this, name);
    }
    
    Emperor.prototype = Object.create(Penguin.prototype);
    Emperor.prototype.constructor = Emperor;