JavaScript原型(二)
原型链
在JavaScript原型(一)中,我们介绍了每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
基本概念
如果原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条,即原型链。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
以上述代码为例,其原型链如下图所示。可以看出SuperType的prototype指向其原型对象,SubType的原型是一个SuperType实例,而每个SuperType都会指向其构造函数的原型对象,这样就形成了原型链。
在JavaScript中,每个对象都继承自Object,因此,上述原型链的完整结构如下图所示
可以通过instanceof 操作符确定原型和实例之间的关系。只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回true。
alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true
原型式继承
ECMAScript 5 通过新增Object.create()方法规范化了原型式继承。这个方法接收两个参数:
- 一个用作新对象的原型对象(可选的)
- 另一个为新对象定义额外属性的对象
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
var anotherPerson2 = Object.create(person, {
name: {
value: "Greg"
}
});
alert(anotherPerson2.name); //"Greg"
原型式继承,可以在不必预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅复制。而复制得到的副本还可以得到进一步改造。需要注意的是,包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。
还有一点需要注意,即在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链。原型链虽然很强大,可以用它来实现继承,但它也存在一些问题。其中,最主要的问题来自包含引用类型值的原型。
参考:《JavaScript高级程序设计第三版》