直接使用prototype原型设计类的继承存在两个问题:
- 由于构造函数事先声明,而原型属性在类结构声明之后才被定义,因此无法通过构造函数参数向原型动态传递参数。这样实例化对象都是一个模样,没有个性。要改变原型属性值,则所有实例都会受到干扰。
- 当原型属性的值为引用类型数据时,如果在一个对象实例中修改该属性值,将会影响所有的实例。
【示例1】简单定义Book类型,然后实例化。
function Book(){ } //声明构造函数
Book.prototype.o = {x:1,y:2} //构造函数的原型属性o是一个对象
var book1 = new Book(); //实例化对象book1
var book2 = new Book(); //实例化对象book2
console.log(book1.o.x); //返回1
console.log(book2.o.x); //返回1
book2.o.x = 3; //修改实例化对象book2中的属性x的值
console.log(book1.o.x); //返回3
console.log(book2.o.x); //返回3
由于原型属性o为一个引用型的值,因此所有实例的属性o的值都是同一个对象的引用,一旦o的值发生变化,将会影响所有实例。
构造原型正是为了解决原型模式而诞生的一种混合设计模式,它把构造函数模式与原型模式混合使用,从而避免了此类问题的发生。
实现方法:对于可能会相互影响的原型属性,并且希望动态传递参数的属性,把它们独立出来使用构造函数模式进行设计。对于不需要个性设计、具有共性的方法或属性,则使用原型模式来设计。
【示例2】遵循上述设计原则,把其中两个属性设计为构造函数模式,设计方法为原型模式。
function Book(title,pages){ //构造函数模式设计
this.title = title;
this.pages = pages;
}
Book.prototype.what = function(){ //原型模式设计
console.log(this.title +this.pages);
};
var book1 = new Book("JavaScript程序设计",160);
var book2 = new Book("C程序设计",240);
console.log(book1.title);
console.log(book2.title);
构造原型模式是ECMAScript定义类的推荐标准。一般建议使用构造函数模式定义所有属性,使用原型模式定义所有方法。这样所有方法都只创建一次,而每个实例都能够根据需要设置属性值。这也是使用最广的一种设计模式。