变量属性的继承

frr*_*lod 2 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");
Run Code Online (Sandbox Code Playgroud)

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

阅读完评论之后,我编辑了一个问题:你可以看到我在创作时给出了一个名字emp,而且emp.name确实是我的新emp的名字,即"Empy".但是name从Penguin类构造函数继承的呢?它去哪儿了?

Aad*_*hah 8

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

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

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

function Penguin(name) {
    this.name = name;
    this.numLegs = 2;
}
Run Code Online (Sandbox Code Playgroud)

然后创建一个Emperor继承自Penguin以下内容的构造函数:

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

Emperor.prototype = new Penguin();
Run Code Online (Sandbox Code Playgroud)

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

  1. Emperor.prototype.name = undefined- 这是因为你没有传递namePenguin构造函数.如果我写的Emperor.prototype = new Penguin("Empy")Emperor.prototype.name就是"Empy".
  2. Emperor.prototype.numLegs = 2 - 显然.

现在,当你创建一个新的Emperor如下,你需要给构造函数的名称(请记住,你继承undefinedPenguin作为名字).因此:

var emp = new Emperor("Empy");
Run Code Online (Sandbox Code Playgroud)

那是旧学校的方法.

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

function Emperor(name) {
    Penguin.call(this, name);
}

Emperor.prototype = Object.create(Penguin.prototype);
Emperor.prototype.constructor = Emperor;
Run Code Online (Sandbox Code Playgroud)

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

  1. 我们继承了使用的原型成员,而不是创建一个新的Penguin使用实例new Penguin()并且不向它传递任何参数.这也可以防止基本构造函数的不必要的初始化,直到实际调用派生的构造函数.PenguinObject.create(Penguin.prototype)
  2. this.name = name我们不再在派生的构造函数中写入,而是使用Penguin.call(this, name)它来为我们调用基础构造函数.这种模式称为mixin,如果基本构造函数需要在运行时初始化或需要维护自己的状态信息,则非常有用.

请注意,我们还添加了一个附加声明Emperor.prototype.constructor = Emperor.这是因为它prototype是一个非常特殊的属性,存在于所有函数中.它指向一个具有非常特殊constructor属性的对象,该属性指向函数本身.通过设置Emperor.prototype其他东西,我们失去了这个属性.因此我们再次设置它.

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


Fel*_*ing 5

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

JavaScript中的原型继承非常简单,没有隐藏的魔法.你之间的唯一连接Emperor,并Penguin在这一行:

Emperor.prototype = new Penguin();
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

看看我的答案,我在这里创建了一些很好的ASCII图表来解释实例和它们的原型之间的关系.


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

这时你其实并不想创建的新实例Penguin,你只需要挂钩Penguinprototype成原型链.您可以通过以下方式轻松完成Object.create:

Emperor.prototype = Object.create(Penguin.prototype);
Emperor.prototype.constructor = Emperor;
Run Code Online (Sandbox Code Playgroud)

但是新Emperor物品不再具有numLegs属性了.这就是为什么你必须Penguin在每个新Emperor实例上调用它,就像在其他语言中一样super():

function Emperor (name){
    Penguin.call(this, name);
} 
Run Code Online (Sandbox Code Playgroud)