使用预定义对象的能力只是面向对象语言的能力的一部分,它真正强大之处在于能够创建自定义的类和对象.有以下几种创建方式:
1.工厂方式
我们可以这样定义一辆车:
var car1 = new Object; car1.color='red'; car1.doors=4; car1.showColor=function(){ return this.color; }
此时,我们创建了一辆车,但是,当我们想创建第二辆,第三辆车时,该如何写呢?再写一遍类似重复的代码?不是,此时我们就可以使用工厂方式:
function CreateCar(){ var car1 = new Object; car1.color='red'; car1.doors=4; car1.showColor=function(){ return this.color; } return car1; }
现在,若我们需要car,则只需要做如下操作就可以了:var car1 = CreateCar();var car2 = CreateCar();此时,我们获得的车的属性都是固定的,我们还可以动态的设定车的属性,如下:
function CreateCar(color,doors){ var car1 = new Object; car1.color=color; car1.doors=doors; car1.showColor=function(){ return this.color; } return car1; }
现在,我们再创建车辆时,就可以动态设置car的属性了.
但是对于上面的代码,我们发现,每当我们创建一辆车的时候,车的属性可能不同,但车的showColor方法却是一致的,而我们却每次都要创建一个相同的方法,
我们对上面的类做以下处理:
function showColor(){ return this.color; } function CreateCar(color,doors){ var car1 = new Object; car1.color=color; car1.doors=doors; car1.showColor=showColor; return car1; }
此时,我们基本算是完成了对工厂方式的构建.
2.构造函数方式
创建构造函数方式和创建工厂方式一样简单,我们来看以下实例:
function Car(color,doors){ this.color=color; this.doors=doors; this.showColor=function(){ return this.color; } }
我们注意到,在构造函数中,我们并没有创建对象,而是使用了关键字this,在使用new运算符创建新的对象时,在执行第一行代码前先创建一个对象,只有用this才能访问到该对象,然后直接赋予this属性,默认情况下是构造函数的返回值.就像工厂方式一样,构造函数方式还是会重复生成函数,尽管同样可以使用先定义函数,再引用该函数的形式,但是,这样做,在语意上并没有什么优势.现在,我们来看原型方式.
3.原型方式
该方式利用了对象prototype属性,可把它看成是创建对象所依赖的原型.在这里,是先定义一个空的构造函数来设置类名,然后将所有的属性和方法都定义在prototype属性上,具体见例子:
function Car(){ } Car.prototype.color='red'; Car.prototype.doors=4; Car.prototype.showColor=function(){ return this.color; }
此时,我们已经定义好了一个Car类,现在,当我们使用new Car()时,原型的所有属性和方法都会被立即赋予要创建的对象,这就意味着所有的Car的实例都存放着指向showColor()函数的指针.原型方式有以下缺点:a.构造函数没有参数,无法在建立对象的时候就使用自定义属性.b.实例的所有属性都是被共享的,若其中一个改变,则可能会影响到其他的实例(此处的属性针对的是引用类型的属性).针对b问题,我们来看以下代码:
function Car(){ } Car.prototype.drivers = new Array("张三","李四"); var car1 = new Car(); var car2 = new Car(); car1.drivers.push("王五"); document.write(car2.drivers);
注意:此处,我们在car1上增加了一个驾驶员,现在,我们输出的结果是car2的驾驶员.结果如何呢,让我们来看看.
我们发现,car2的驾驶员也增加了一个,从而造成了结果并非是我们预期的结果.
现在,我们使用混合的构造函数/原型方式来创建对象,就可以避免这个问题.
4.构造函数/原型方式
使用这种构造方式也很简单,主要就是:构造函数定义所有非函数的属性,原型定义所有的函数属性.结果就是所有的函数都仅仅被创建一次,而每个对象都拥有自己的对象属性实例.见代码:
function Car(color,doors){ this.color=color; this.doors=doors; this.drivers=new Array("张三","李四"); } Car.prototype.getColor=function(){ return this.color; }
现在,我们来使用new Car操作,再为实例增加一个driver,发现增加的driver并没有影响到所有的实例,从而达到了我们想要的效果.
5.动态原型方法
对于习惯了使用高级编程语言的人来说,定义一个类,应该是方法和属性都在类体内,而现在属性在类体内,而方法在类体外建立,感觉似乎有些不太和谐.于是就出现了动态原型方法.动态原型方法和构造函数/原型方法的基本方法基本上相同,唯一的区别就是赋予对象方法的位置不同,见例子:
function Car(color,doors){ this.color=color; this.doors=doors; this.drivers=new Array("张三","李四"); if(typeof Car._init=="undefined"){ Car.prototype.getColor=function(){ return this.color; } Car._init=true; } }
现在执行 new Car('red',4),在执行typeof Car._init=='undefined'之前,这个方法都一直在正常运行,直到这段代码,如果这个值未定义,构造函数将用原型方式定义对象方法,然后将Car._init属性定义为true,那么再此创建实例时,Car._init属性已经为true,就不会再重新对象原型方法了.
6.混合工厂方法
混合工厂方法和工厂方式几乎一致,它同样是先构造对象,然后再往对象中添加属性和方法,唯一的区别就是混合工厂方法在生成对象的时候依旧使用new关键字.
见代码:
function CreateCar(color,doors){ var car1 = new Object; car1.color=color; car1.doors=doors; car1.showColor=function(){ return this.color; }; return car1; } var c = new CreateCar('red',4);
混合工厂方法和工厂方式及经典方式(构造函数,原型方式)一样都会产生问题,了解即可,不建议使用.
以上介绍了6种构造方式,目前使用最广泛的还是构造函数/原型方式.此外,动态原型方式也很流行,在功能上和构造函数/原型方式等价.
不过不要单独使用经典的构造函数方式或者原型方式.