2009-05-21 143 views
162

我是JavaScript OOP的新手。你能解释下面的代码块之间的区别吗?我测试过,并且两个块都可以工作最佳做法是什么?为什么?了解JavaScript中的原型继承

第一块:

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笔者添加DriveFly方法,但不声明为this.Drive方法内部Car类和this.FlySuperCar类?

为什么SuperCar.prototype.constructor需要设置回SuperCar?当设置了prototype时,构造函数属性是否被覆盖?我评论了这条线,没有任何改变。

为什么在SuperCar构造函数中调用Car.call(this, name);?会不会性能和Car方法“继承”当我做

var myCar = new Car("Car"); 
+3

更现代的方法是使用的Object.create( )而不是新的:http://ncombo.wordpress.com/2013/07/11/javascript-inheritance-done-right/ – Jon 2013-07-11 13:53:13

+2

这个例子可能会让人困惑:在这种情况下,“超级”是指飞行的汽车,而OOP中的“超级”通常是指父级原型/类。 – mikemaccana 2014-04-28 09:53:48

+2

我认为让JavaScript的原型继承难以理解的部分是它将它与构造函数混合在一起的地方。如果我们删除构造函数和类的概念,并且只有对象,会怎样?每个对象要么从空(null)或从另一个对象继承。这将会更容易理解,正是Object.create()所做的。 – 2014-07-08 18:00:09

回答

78

两个区块的方式,在第一个例子Drive()只会出现一次,而在第二种方法Drive()将每个实例存在差异(每次你做new Car()drive()将再次创造的功能)。或者不同的说,第一个使用原型来存储函数,第二个使用构造函数。查找函数是构造函数,然后是原型。因此,对于Drive()的查找,无论它在构造函数还是原型中,都会发现它。使用原型更高效,因为通常每个类型只需要一次函数。

在JavaScript中调用new会自动在原型中设置构造函数。如果您覆盖原型,则必须手动设置构造函数。

JavaScript中的继承没有什么像super。所以如果你有一个子类,唯一的机会就是调用超级构造函数的名字。

1

我不是100%肯定,但我相信不同的是,第二个例子只是复制了汽车类的内容到超级跑车对象,而第一个将SuperCar原型链接到Car类,以便Car类的运行时更改也影响SuperCar类。

136

要添加到Norbert Hartl's answer,不需要SuperCar.prototype.constructor,但有些人使用它作为获取对象的构造函数(本例中为SuperCar对象)的便捷方式。

刚刚从第一个例子,Car.call(此,名)是超级跑车的构造函数,因为当你这样做:

var mySuperCar = new SuperCar("SuperCar"); 

这是JavaScript的作用:

  1. 一新鲜的空白对象被实例化。
  2. 新对象的内部原型设置为Car。
  3. 运行SuperCar构造函数。
  4. 完成的对象返回并设置在mySuperCar中。

请注意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); 
} 

作为一个功能,汽车被允许与被调用函数的call method,改变的this含义汽车内的我们正在建立SuperCar实例。普雷斯托!现在每个SuperCar都获得它自己的Name属性。

要结束,在SuperCar构造函数中Car.call(this, name)为每个新的SuperCar对象提供了它自己的唯一的Name属性,但是没有重复已经在Car中的代码。

一旦你理解了原型,原型并不会让人感到恐惧,但它们并不像传统的类/继承OOP模型那样。我写了一篇关于the prototypes concept in JavaScript的文章。它是为使用JavaScript的游戏引擎编写的,但它与Firefox使用的JavaScript引擎相同,因此它应该都是相关的。希望这可以帮助。

8

诺伯特,你应该注意到,你的第一个例子几乎是道格拉斯克罗克福德所说的伪古典继承。什么事情需要注意一下:

  1. 您将从SuperCar.prototype =新车()线和其他从“构造偷”行Car.call(这个叫车的构造两次,一次.. 。您可以创建一个辅助方法来继承原型,而您的Car构造函数只需要运行一次,从而提高设置效率。
  2. SuperCar.prototype.constructor = SuperCar行允许您使用instanceof标识构造函数。有些人希望这个其他人只是避免使用instanceof
  3. 参考变量如:var arr = ['one','two']在super(例如Car)上定义时将被所有实例共享,这意味着inst1.arr。 PU sh ['three'],inst2.arr.push ['four']等,将显示所有实例!本质上,你可能不想要的静态行为。
  4. 你的第二个块在构造函数中定义了fly方法。这意味着每次调用它时,都会创建一个“方法对象”。更好地使用方法的原型!你可以将它保留在构造函数中,如果你愿意的话 - 你只需要保护,所以你只需要实际初始化原型文件(伪):if(SuperCar.prototype.myMethod!='function')...然后定义你的原型文字。 '为什么要调用Car.call(这个,名字)....':我没有时间仔细查看你的代码,所以我可能是错的,但这通常是为了让每个实例都能保持它自己的状态以解决我上面描述的原型链的'staticy'行为问题。

最后,我想提一提,我有TDD的JavaScript代码继承几个例子是在这里工作:TDD JavaScript Inheritance Code and Essay我很想得到,因为我希望能改善它,并保持你的反馈,开源。我们的目标是帮助古典程序员快速掌握JavaScript,并补充Crockford和Zakas书籍的研究。对于功能ABC创建

abc.prototype.testProperty = 'Hi, I am prototype property'; 
abc.prototype.testMethod = function() { 
    alert('Hi i am prototype method') 
} 

创建新实例的功能ABC

0
function abc() { 
} 

原型的方法和属性

var objx = new abc(); 

console.log(objx.testProperty); // will display Hi, I am prototype property 
objx.testMethod();// alert Hi i am prototype method 

var objy = new abc(); 

console.log(objy.testProperty); //will display Hi, I am prototype property 
objy.testProperty = Hi, I am over-ridden prototype property 

console.log(objy.testProperty); //will display Hi, I am over-ridden prototype property 

http://astutejs.blogspot.in/2015/10/javascript-prototype-is-easy.html