- 原文:Create Advanced Web Applications With Object-Oriented Techniques
- 译文(因文章较长,分多篇贴出,此篇为第4部分):
原型
原型对象是JavaScript面向对象编程的中心概念。其称谓来源于JavaScript中对象的创建,都是作为一个现有的样例(即原型)的副本。这个原型对象的任何属性和方法,都会作为这个现有的原型构造器所创建的对象的属性和方法出现的。也可以说这些对象都从它的原型那里继承了所有属性和方法。例如当你这样样创建一个Dog对象的时候:
var buddy = new Dog("Buddy");
那个被buddy引用的对象从它的原型继承了所有属性和方法,尽管简单的一行代码可能并不足以看清原型的来源。对象buddy的原型来自一个构造函数的原型(在这个例子中,就是函数Dog)。
在JavaScript中,每个函数都有一个指向到原型对象的,叫”prototype”的属性。反过来,这个原型对象又会有一个叫”constructor”的属性,它指向回函数本身。这是一种环形的映射关系。为了更好地理解这个环形关系,请看插图 Figure3。

Figure3 每个函数的原型都有一个Constructor属性
现在我们知道了,当一个函数(就拿上面的Dog的例子来说)使用”new”关键字来创建对象时,所得的结果对象就将会继承到Dog.prototype的属性。在Figure3,我们会看到,Dog.prototype对象有一个指回Dog函数的constructior属性。因此,每一个Dog对象(从Dog.prototype继承来的)也同样会有一个指回Dog函数的constructior属性,代码就 Figure 4 确认了这一点。Figure 5描述了在构造函数、原型对象以及他们所建立的对象之间的关系。

Figure 5 实例均从Prototype那里继承而来
有的人可能已经注意到Figure 4中调用了两个函数hasOwnProperty 和isPrototypeOf 。它们从哪里来的呢?它们并不来自Dog.prototype。事实上Dog.prototype还有其它的像toString、toLocaleString和valueOf这样的函数供我们调用,但它们都不来自Dog.prototype。这就像是.NET Framework有System对象那样,是一个道理。(在.NET Framework中)Object类是所有类的根基,JavaScript就有一个类似的是所有原型的根基:Object.prototype。(Object.prototype的原型是null。)
切记,在这个例子中的Dog.prototype就是一个对象,是通过对Object构造器函数的调用来创建的,虽然这并非显式的:
Dog.prototype = new Object();
所以,就像Dog的实例继承自Dog.prototype那样,Dog.prototype继承自Object.prototype。这就使得所有Dog实例都从Object.protoype那里继承到其属性和方法了。
所有的JavaScript对象都有一条以Object.prototype为终点的继承链。注意,目前为止我们所看到的这些继承都是发生在实时对象中的继承。这点与大家平时的继承概念有所区别,并不是在当类在声明时发生在类之间发生的。所以JavaScript的类的继承更具有动态性。这点是通过简单的算法来实现的:当你访问一个对象的属性/方法时,JavaScript会查看这个属性/方法是否在对象中定义有的。如果不是,则检查对象的原型;再不是的话,就检查对象的原型的原型,就这样一直检查下去,直到Object.prototype为止。插图 Figure 6就是个处理过程的关系图了。

Figure 6 toString()方法在原型链中的分析过程(点击查看大图)
从这种JavaScript动态的属性访问及函数调用,可以得到以下几个推论:
- 对原型对象的改变也对继承它的对象可见,即使(继承它的)这些对象已经被建立了;
- 如果你为对象建立一个名为X的属性/方法,与其同名的在原型对象的属性/方法会从中隐藏起来。对于Dog.prototype实例来说,你可以定义一个toString方法去重载掉Object.prototype的toString方法;
- 改变是单向,从原型到其子对象,但不能相反。
Figure 7就举例说明了这个推论。 Figure 7同样展示了一个解决我们之前所遇到的,冗余方法的对策。除了为每个对象使用独立的函数对象外,你可以通过把方法放进原型里让其它对象去共享它。在这个例子中,方法getBreed 就被 rover和spot共享到了–总之就直到你在spot中把toString给重载掉(译者注:例子中重载掉的是getBreed才对)。之后,spot就有了自己版本的getBreed方法了,不过rover对象和后来的用new GreateDane创建的对象都是共享同一个getBreed方法,即在GreatDane.prototype对象中定义的那个方法。
To be Continued…


Leave a Reply