根据JavaScript高级程序设计一书的第六章以及JavaScript面向对象编程总结。
在对JS的继承方式进行总结之前,先要了解一下创建对象的几种方式:工厂模式、构造函数模式、原型模式、组合模式、动态原型模式以及他们的优缺点。
传统模式是采用Object()构造函数和对象字面量的方法创建对象,但是这样会产生大量重复代码,效率较低,于是就有了工厂模式
工厂模式
工厂模式是定义一个函数,然后在函数里面新建一个对象,然后返回该对象。
function createShape(name){
var o=new Object();
o.name=name;
o.getName=function(){
alert(this.name);
}
return o;
}
var twoDShape=createShape('2D Shape');
var triangle=createShape('Triangle');
这样不是新建一个对象,而是调用了一个函数,这样这两个新建对象之间没有任何联系,不能反映他们是同一个原型的实例(对象的识别问题),于是有了构造函数模式。
构造函数模式
构造函数模式就是定义一个函数,函数名首字母大写(约定俗成),没有显示的创建对象,也没有返回一个对象,使用了new创建对象,这样就解决了对象识别问题,他们都是构造函数的实例对象,可以用constructor属性确定。
function Shape(name){
this.name=name;
this.getName=function(){
alert(this.name);
}
}
var twoDShape=new Shape('2D Shape');
var triangle=new Shape('Triangle');
alert(twoDShape.constructor===Shape);//true
alert(triangle.constructor===Shape);//true
但是这样一来,twoDShape和triangle都含有getName()这个方法,如果有多个实例创建,则同一种方法会在多个实例对象中重复创建,于是有了原型模式。
原型模式
如果不了解原型链可以看一看js原型链。
原型模式就是将所有属性和方法写到原型对象中,构造函数变成了空函数,所有的属性和方法都是共享的。
function Shape(name){
}
Shape.prototype={
constructor:Shape,
name:'shape',
shape:['2D Shape','Triangle'],
getName:function() {
alert(this.name);
}
};
var twoDShape=new Shape();
var triangle=new Shape();
twoDShape.getName();//shape
triangle.getName();//shape
原型模式也有缺点,对于原型中有引用类型值的属性比如数组、对象等,当修改其中一个实例的属性时,其余的实例的该属性也会发生变化。
twoDShape.shape.push('Square');
alert(twoDShape.shape);//'2D Shape,Triangle,Square'
alert(triangle.shape);//'2D Shape,Triangle,Square'
对shape属性进行添加,会发现2个实例的属性都改变了,故单独使用原型模式不是明智的。
组合模式
组合模式是将构造函数与原型模式进行组合,将需要共享的方法写到原型对象中,而构造函数用来定义基本的属性值,这也是大多数人都会用的模式。
function Shape(name){
this.name=name;
this.shape=['2D Shape','Triangle'];
}
Shape.prototype={
constructor:Shape,
getName:function(){
alert(this.name);
}
};
var twoDShape=new Shape('2D Shape');
var triangle=new Shape('Triangle');
twoDShape.shape.push('Square');
alert(twoDShape.shape);//'2D Shape,Triangle,Square'
alert(triangle.shape);//'2D Shape,Triangle'
动态原型模式
动态原型模式是将构造函数和原型对象进行封装,使它符合面向对象语言的同时,保持它们的优点。
function Shape(name){
this.name=name;
if(typeof this.getName !="function"){
Shape.prototype.getName=function(){
alert(this.name);
}
}
}
var twoDShape=new Shape('2D Shape');
var triangle=new Shape('Triangle');
twoDShape.getName();//2D Shape
triangle.getName();//Triangle
将所有的属性和方法均封装在构造函数中,并且通过if判断原型对象是否初始化,这样只用执行一次赋值操作。
在使用动态原型模式时,不能重写原型对象,这样会使之前创建的方法和属性都无效。
其实JavaScript高级程序设计上还讲了寄生构造函数模式和稳妥构造函数模式。寄生构造函数与工程模式是一样的,只不过在创建实例的时候用了new,在有其他的选择下不建议使用这种模式。稳妥构造函数模式与寄生构造函数模式基本是一样的,不过在新创建实例对象时不用this以及new调用构造函数,这种模式适用于某些安全执行环境。