剖析JavaScript中的原型(Prototype)
上篇提到构造函数包含一个prototype是实现继承的关键,就是原型链的概念。在JavaScript中当使用构造函数创建一个对象时,如下面的图示(来自<<JavaScript高级程序设计>>):
JavaScript为建构函数生成一个原型(Prototype)指向原型对象,包含了所有实例共享的对象。而原型中也有一个constructor指向建构函数,以起到标识的作用,说明”我是谁”. 建构函数本身也是一个普通函数,只是首字母大写了。
当通过访问实例的属性和方法时:
a.如果实例定义了同名的属性或方法,使用之。
b.否则,尝试调用原型的同名属性或方法。
这里有一个关键的准则:
所有在建构函数中定义的属性和方法,各个实例各自拥有一份。
所有在原型中定义的引用类型属性(基本类型除外)和方法,各个实例将共享使用。
下面是一个包含建构和原型两种方式的对象创建示例:
function Person(name)
{
this.name = name;
}
Person.prototype =
{
friends:["A","B"],
introduceSelf : function()
{
document.write("<p /> My name is "+this.name);
document.write("<p /> My friends are "+this.friends);
}
};
var horky = new Person("Horky");
horky.friends.push("C");
var arthas = new Person("Arthas");
horky.introduceSelf(); //My friends are A,B,C
arthas.introduceSelf(); //My friends are A,B,C
输出的结果是:
My name is Horky
My friends are A,B,C
My name is Arthas
My friends are A,B,C
认识这个特性很重要。一般我们希望类的方法可以重用,而不是各个实例再拥有一份实现,就可以放到原型的定义里面去。而对于子类需要自己定义的属性就可以放到建构函数中去。
既然原型也是一个对象,就可以按需要修改,比如扩展一下类的功能,随时加一个定义就可以了。
在上面代码后加入类似下面的代码:
……
horky.introduceSelf(); //My friends are A,B,C
arthas.introduceSelf(); //My friends are A,B,C
Person.prototype.saySomething = function(text)
{
document.write("<p /> "+this.name+" says:"+text);
}
horky.saySomething("Hello, everyone!");
输出结果就会多一行:
Horky says:Hello, everyone!
理解了原型,我们再看一下继承。继承的要素是继承父类的属性和方法定义,而且属性可以自行更改。本身实例和原型之间就是继承关系的影子,因为访问是从下至上的。所以继承无非是要将子类的prototype变成一个指向父类原型的对象就可以了,如下图:
这就是<<JavaScript高级程序设计>>中所讲的较常用的寄生组合式继承。寄生指的是调用建构函数,组合则指的原型的运用。下面是示例代码:
function inheritPrototype(subType,superType){
var prototype=object(superType.prototype); //创建对象
prototype.constructor=subType; //增强对象
subType.prototype=prototype; //指定对象
}
function SuperType(name){
this.name=name;
this.color=["red","blue","greed"];
}
SuperType.prototype.sayName=function(){
alert(this.name);
}
function SubType(name,age){
SuperType.call(this,name); //呼叫父类的建构函数
this.age=age;
}
inheritPrototype(SubType,SuperType); //将子类设定为继承自父类
SubType.prototype.sayAge=function(){ //增加一个子类方法
alert(this.age);
};
只要掌握前面所提原型的概念,JavaScript中使用很多OO的实现就很好理解了。比如所谓的模块(单件)模式之类的。
*<<JavaScript高级程序设计>>第3版在这段代码下(P173)附的图6-6是属于前段代码的解释,放在这段代码下实在容易让人误解。