了解JavaScript中的原型inheritance
我是JavaScript OOP的新手。 你能解释下面的代码块之间的区别吗? 我testing过,两个块都可以工作。 最佳做法是什么?为什么?
第一块:
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
类中?
为什么SuperCar.prototype.constructor
需要重新设置为SuperCar
? prototype
设置时,构造函数属性是否被覆盖? 我注释掉了这一行,没有任何改变。
为什么叫Car.call(this, name);
在SuperCar
构造函数? 当我这样做的时候Car
属性和方法不会被“inheritance”吗?
var myCar = new Car("Car");
这两个模块的不同之处在于,第一个例子中, Drive()
只会存在一次,而第二个方法Drive()
将会存在于每个实例中(每次你做new Car()
函数时, drive()
都会被重新创build) 。 或者不同的说第一个使用原型来存储函数和第二个构造函数。 函数的查找是构造函数,然后是原型。 因此,对于Drive()
查找,无论它在构造函数还是原型中,都可以find它。 使用原型更高效,因为通常每个types只需要一个函数。
JavaScript中的new
调用会自动将构造函数设置为原型。 如果覆盖原型,则必须手动设置构造函数。
在javascript中的inheritance没有像super
。 所以如果你有一个子类,唯一的机会就是调用超级构造函数。
为了增加Norbert Hartl的答案 ,SuperCar.prototype.constructor是不需要的,但有些人使用它作为获取对象的构造函数(在这种情况下是SuperCar对象)的方便方式。
从第一个例子来看,Car.call(this,name)在SuperCar构造函数中,因为当你这样做的时候:
var mySuperCar = new SuperCar("SuperCar");
这是JavaScript所做的:
- 一个新鲜的空白对象被实例化。
- 新对象的内部原型被设置为Car。
- SuperCar构造函数运行。
- 完成的对象返回并设置在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); }
哎呀,你永远不想改变这个特殊的对象引用。 记得4个步骤? 挂在JavaScript提供的对象上,因为这是保持SuperCar对象与Car之间宝贵的内部原型链接的唯一方法。 那么我们如何设置Name,而不必重复自己,并且不会丢掉我们新鲜的SuperCar对象JavaScript花了这么多特殊的努力来为我们做准备?
两件事情。 一:这个意思是灵活的。 二:汽车是一种function。 有可能调用Car,而不是用一个原始的,新鲜的实例化对象,而是用一个SuperCar对象。 这给了我们最终的解决scheme,这是你的问题的第一个例子的一部分:
function SuperCar(name){ Car.call(this, name); }
作为一个函数,可以使用函数的调用方法调用Car,Car 方法将Car内部的含义更改为我们正在构build的SuperCar实例。 普雷斯托! 现在每个SuperCar都有自己的Name属性。
总结一下,SuperCar构造函数中的Car.call(this, name)
为每个新的SuperCar对象提供了它自己的唯一的Name属性,但是没有重复Car中的代码。
一旦你理解原型,原型就不会吓人,但它们不像经典的类/inheritanceOOP模型。 我写了一篇关于JavaScript中原型概念的文章。 它是为使用JavaScript的游戏引擎编写的,但它与Firefox使用的是相同的JavaScript引擎,所以它应该都是相关的。 希望这可以帮助。
诺伯特,你应该注意到,你的第一个例子几乎是道格拉斯·克罗克福德所称的伪古典inheritance。 有些事情要注意这一点:
- 您将调用Car构造函数两次,一次来自SuperCar.prototype = new Car()行,另一次来自“构造函数窃取”行Car.call(这…您可以创build一个辅助方法来inheritance原型,而您的汽车制造商只需要运行一次,使设置更有效率。
- SuperCar.prototype.constructor = SuperCar行将允许您使用instanceof来标识构造函数。 有些人希望这个其他人只是避免使用instanceof
- 参考variables如:var arr = ['one','two']在super(例如Car)上定义时将被所有实例共享。 这意味着inst1.arr.push ['three'],inst2.arr.push ['four']等,将显示所有实例! 本质上,你可能不想要的静态行为。
- 你的第二个块在构造函数中定义了fly方法。 这意味着每次被调用时,都会创build一个“方法对象”。 更好地使用方法的原型! 如果你愿意的话,你可以把它保留在构造函数中 – 你只需要保护,所以你只需要实际初始化原型文件(伪):if(SuperCar.prototype.myMethod!='function')…然后定义你的原型文字。
- '为什么叫Car.call(这个,名字)….':我没有时间仔细查看你的代码,所以我可能是错的,但通常这样每个实例都可以保持它自己的状态来修复我上面描述的原型链的'staticy'行为问题。
最后,我想提一下,我在这里有几个TDD JavaScriptinheritance代码的例子: TDD JavaScriptinheritance代码和散文我希望得到您的反馈,因为我希望改进它,并保持开源。 我们的目标是帮助古典程序员快速掌握JavaScript,同时补充Crockford和Zakas书籍的研究。
我不是100%确定,但我相信不同的是,第二个例子只是将Car类的内容复制到SuperCar对象中,而第一个例子将SuperCar原型链接到Car类,以便将运行时更改为汽车类也会影响SuperCar类。
function abc() { }
为函数abc创build的原型方法和属性
abc.prototype.testProperty = 'Hi, I am prototype property'; abc.prototype.testMethod = function() { alert('Hi i am prototype method') }
为函数abc创build新的实例
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