了解JavaScript中的原型继承
我是JavaScript OOP的新手。 你能解释下面的代码块之间的区别吗? 我测试过,并且两个块都可以工作 最佳做法是什么?为什么?
第一块:
function Car(name){
this.Name = name;
}
Car.prototype.Drive = function(){
document.write("My name is " + this.Name + " and I'm driving. <br />");
}
SuperCar.prototype = new Car();
SuperCar.prototype.constructor = SuperCar;
function SuperCar(name){
Car.call(this, name);
}
SuperCar.prototype.Fly = function(){
document.write("My name is " + this.Name + " and I'm flying! <br />");
}
var myCar = new Car("Car");
myCar.Drive();
var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();
第二块:
function Car(name){
this.Name = name;
this.Drive = function(){
document.write("My name is " + this.Name + " and I'm driving. <br />");
}
}
SuperCar.prototype = new Car();
function SuperCar(name){
Car.call(this, name);
this.Fly = function(){
document.write("My name is " + this.Name + " and I'm flying! <br />");
}
}
var myCar = new Car("Car");
myCar.Drive();
var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();
为什么作者使用prototype
添加Drive
和Fly
方法,但是并没有将它们声明为Car
类中的this.Drive
方法, this.Fly
在SuperCar
类中使用this.Fly
?
为什么SuperCar.prototype.constructor
需要重新设置为SuperCar
? prototype
设置时是否覆盖构造函数属性? 我评论了这条线,没有任何改变。
为什么叫Car.call(this, name);
在SuperCar
构造函数中? 当我这样做的时候Car
属性和方法不会被“继承”
var myCar = new Car("Car");
这两个模块的不同之处在于,第一个示例中Drive()
仅存在一次,而第二个Drive()
将存在于每个实例中(每当您执行new Car()
,函数drive()
将再次创建) 。 或者不同的说,第一个使用原型来存储函数,第二个使用构造函数。 查找函数是构造函数,然后是原型。 因此,对于Drive()
查找,无论它在构造函数还是原型中,都可以找到它。 使用原型更高效,因为通常每种类型只需要一次函数。
JavaScript中的new
调用自动将构造函数设置为原型。 如果您覆盖原型,则必须手动设置构造函数。
在JavaScript中的继承没有像super
。 所以如果你有一个子类,唯一的机会就是调用超级构造函数的名字。
要添加到Norbert Hartl的答案中,SuperCar.prototype.constructor不是必需的,但有些人使用它作为获取对象构造函数的便捷方式(本例中为SuperCar对象)。
从第一个例子来看,Car.call(this,name)在SuperCar构造函数中,因为当你这样做时:
var mySuperCar = new SuperCar("SuperCar");
这就是JavaScript所做的:
注意JavaScript没有给你打电话。 原型就像它们一样,任何你不为SuperCar设置的属性或方法都将在Car中查找。 有时候这很好,例如SuperCar没有Drive方法,但它可以共享Car的方法,因此所有的SuperCar都将使用相同的Drive方法。 其他时候你不想分享,就像每个SuperCar拥有自己的名字一样。 那么,如何将每个SuperCar的名字设置为它自己的东西呢? 你可以在SuperCar构造函数中设置this.Name:
function SuperCar(name){
this.Name = name;
}
这有用,但请稍等。 我们不是在Car构造函数中做同样的事吗? 不想重复自己。 既然Car已经设置了名字,那就让我们来调用它。
function SuperCar(name){
this = Car(name);
}
哎呦,你永远要改变特殊this
对象的引用。 记住4个步骤? 挂在JavaScript提供的对象上,因为这是保持SuperCar对象与Car之间宝贵的内部原型链接的唯一方法。 那么,我们如何设置Name,而不必重复自己,也不会丢掉我们新鲜的SuperCar对象JavaScript花了很多特殊的努力为我们做准备?
两件事情。 一:的意思this
是灵活的。 二:汽车是一种功能。 可以调用Car,而不是用一个原始的,新鲜的实例化对象,而是用一个SuperCar对象。 这给了我们最终的解决方案,这是您问题中第一个示例的一部分:
function SuperCar(name){
Car.call(this, name);
}
作为一个功能,汽车被允许与函数的调用方法,它改变的意义被调用this
车内我们打造了超级跑车的实例。 普雷斯托! 现在每个SuperCar都获得它自己的Name属性。
总结一下,SuperCar构造函数中的Car.call(this, name)
为每个新的SuperCar对象提供了它自己的唯一的Name属性,但不需要复制Car中已有的代码。
一旦你理解原型,原型就不会吓人,但它们根本不像经典的类/继承OOP模型。 我写了一篇关于JavaScript中原型概念的文章。 它是为使用JavaScript的游戏引擎编写的,但它与Firefox使用的JavaScript引擎相同,因此它应该都是相关的。 希望这可以帮助。
诺伯特,你应该注意到,你的第一个例子几乎就是道格拉斯克罗克福德所称的伪古典继承。 需要注意的是:
最后,我想提一下,我在这里有几个TDD JavaScript继承代码的例子:TDD JavaScript继承代码和论文我希望得到您的反馈,因为我希望改进它并保持开源。 我们的目标是帮助古典程序员快速掌握JavaScript,并补充Crockford和Zakas书籍的研究。
链接地址: http://www.djcxy.com/p/30051.html