JavaScript OOP(2)定义JavaScript类

JavaScript OOP(2)定义JavaScript类
5.1.7 定义类的方式(工厂方式、构造函数、原型方式、混合方式)

在面向对象的开发中,类被认为是对象的模板。在JavaScript中,可以由开发者自定义类及类的方法和属性。

首先学习最基本的制造类的方法,工厂方式。工厂方式的特点是通过特定的工厂方法创建特定类型的对象。典型的工厂方式的实现代码如下:

function createStudnetFactory()

{

var stu=new Object();

stu.ID="1";

stu.Name="hong shi dan";

stu.Skill="Ajax & JavaScript & ASp.NET";

stu.Show=function()

{

alert(stu.Skill);

}

return stu;

}

var stu=createStudnetFactory();

stu.Show();

现在来看看工厂方法的效果,如图5.2所示:

clip_image002

图5.2 工厂方法效果图

可以清楚的看到,对话框显示的正是工厂创建的stu对象的Skill属性的值。实际上,工厂方法可以使用参数来设定所创建对象的各项属性值。带参工厂方法如下所示:

function createStudnetFactory(id,name,skill)

{

var stu=new Object();

stu.ID=id;

stu.Name=name;

stu.Skill=skill;

stu.Show=function()

{

alert(stu.Skill);

}

return stu;

}

var stu=createStudnetFactory(1,"hongbo","Ajax");

stu.Show();

var reader=createStudnetFactory(2,"读者","Ajax");

reader.Show();

带参工厂方法的效果,如图5.3所示:

clip_image004

图5.3 带参工厂方法的效果图

可以清楚的看到,对话框显示的正是工厂方法中传入的skill参数的值。

还有一种与传统面向对象语言更相似的创建对象的方法,即构造函数方式。构造函数方式很像工厂方式,但是构造函数方式可以于new关键字联用。构造函数方式示例代码如下:

function StudentConstract(id,name,skill)

{

this.ID=id;

this.Name=name;

this.Skill=skill;

this.ShowSkill=function()

{

window.alert(this.Skill);

}

}

var stu=new StudentConstract(1,"hongbo","Ajax");//构造函数与new关键字联用

stu.ShowSkill();

在上例中,可以看到构造函数和new关键字的联用进行对象实例化。但是构造函数方式和工厂方式都1个不可以忽略的弊端,每个对象被实例化时,都会实例化类中的方法。这个弊端会造成每个对象都有独立的方法版本。

实际上,还有一种方式可以避免这种弊端。在开发中,可以使用原型方式进行对象的创建。可以通过以下代码实现原型方式:

function Student()

{

//这里可以看做是Student的构造函数

}

Student.prototype.Id="1";

Student.prototype.Name="Hongbo";

Student.prototype.Skill="Ajax";

Student.prototype.Likes=new Array("编码","游戏");

Student.prototype.ShowSkill=function()

{

alert(this.Skill);

}

var stu1=new Student();

stu.ShowSkill();

alert(stu.Likes);//注意这个消息框中的信息

在上例的代码中,首先定义了1个无参的构造函数。然后,通过Student类的prototype添加属性和各种方法。当通过构造函数实例化对象时,所有对象中放入的都是指向属性和方法的指针。通过原型方式,实现了类中属性和方法的共享。

但是原型方式真的就完美无缺了吗?首先,原型方式定义的类没有带参构造函数,实例化对象后需要一一对对象的属性赋值。其次,所有的对象中包含的都是对属性的引用,这意味着所有对象的属性都是对象公有的。当更改任何对象的1个属性时,都会影响其他对象的属性。以下的代码展现了原型方式的缺陷:

function Student()

{

//这里可以看做是Student的构造函数

}

Student.prototype.Id="1";

Student.prototype.Name="Hongbo";

Student.prototype.Skill="Ajax";

Student.prototype.Likes=new Array("编码","游戏");

Student.prototype.ShowSkill=function()

{

alert(this.Skill);

}

var stu1=new Student();

var stu2=new Student();

stu2.Likes.push("旅行");

alert(stu1.Likes); <!--注意这个消息框中的信息-->

alert(stu2.Likes); <!--注意这个消息框中的信息-->

上例运行的效果如图5.4所示:

clip_image006

图5.4 原型方式的缺陷

读者一定会被运行上例的代码的结果惊呆了!读者一定看到了两个相同的消息框,而在代码中实际仅仅更改了stu2的Likes属性,实际上stu1的Likes属性也受到了更改。导致这个结果的原因很简单,stu1和stu2实例化时,它们的Likes属性都仅仅是指向1个数组的引用指针,当数组本身发生变化时,所有指向该数组的指针都会得到修改后的数组。

原型方式、构造方式、工厂方式等等方法都有不可弥补的缺陷,到底那种方式可以完美的实现对象的创建呢?现在使用最多的方式是混合方式,混合方式是指联合使用构造函数和原型方式,就可以像其他面向对象语言一样创建对象了。

混合方式是通过构造函数方式定义对象的所有非函数属性,用原型方式定义对象的函数属性。使用混合方式可以规避构造函数方式和原型方式的缺陷。对象实例化时,所有的属性都是单一对象私有的,而方法则是所有对象公有的。使用混合方式,修改上例的代码如下:

function Student(id,name,skill)

{

this.ID=id;

this.Name=name;

this.Skill=skill;

this.Likes=new Array("编码","游戏");

}//构造函数定义对象的非函数属性

Student.prototype.ShowSkill=function()//通过原型方式定义对象的函数

{

alert(this.Skill);

}

var stu1=new Student(1,"Hongbo","Ajax");

var stu2=new Student(2,"Reader","JavaScript");

stu2.Likes.push("旅行");

stu1.ShowSkill();

stu2.ShowSkill();

alert(stu1.Likes);<!--注意这里的消息框-->

alert(stu2.Likes);

运行以上的代码,可以看到stu1、stu2的Likes属性并不一致,它们保持了各自属性的独立。stu1、stu2调用ShowSkill()方法,可以看到消息框中的消息不一致,即方法识别了this指针。ShowSkill()方法实现了所有对象的公有。

读者看到混合方式时,肯定还是伴随着失望。因为混合方式和传统的面向对象语言还是有很大的差别,混合方式并没有对对象的属性和方法进行严密的封装。在JavaScript中,提供了动态原型方式进行对对象的属性和方法进行严密封装。实现动态原型方法代码如下:

function Student(id,name,skill)

{

this.Id=id;

this.Name=name;

this.Skill=skill;

this.Likes=new Array("编码","游戏");

if(typeof(Student._initialized)=="undefined")

{

Student.prototype.ShowSkill=function()

{

alert(this.Skill);

}

Student._initialized=true;

}

}

var stu1=new Student(1,"hongbo","Ajax");

var stu2=new Student(2,"Reader","JavaScript");

stu2.Likes.push("旅行");

stu1.ShowSkill();

stu2.ShowSkill();

alert(stu1.Likes);

alert(stu2.Likes);

在上例中,运行结果与混合方式是一致的。动态原型方式实现了对象间属性的独立和方法的共享。

实际上,动态原型方式和混合方式非常相似。唯一的不同在于动态原型添加了1个if判断,这里肯定让很多读者不解。typeof(Student._initialized)到底是什么?实际上Student._initialized是由开发者自定义的属性,用于判断当前对象是否被实例化了。

在对象初始化完成前,Student._initialized并没有被声明或实例化,所以Student._initialized一定是undefined,那么就一定会执行if中的代码。If中的代码让对象的ShowSkill函数属性成为1个函数的引用,这样就实现了方法被所有对象公有。

学习了如此多的创造类的方式,到底应该使用那种最好呢?请仔细阅读下一节。

5.1.8 实例以及实例方式使用的注意事项
在面向对象的JavaScript开发中,目前使用最广泛的是混合方式。动态原型方式也能够实现面向对象的基本特征,并且越来越流行。在开发中,可以采用这两种方式中的任何一种。尽量不要单独使用工厂方式、构造函数方式、原型方式的任何一种,否则会给开发带来不可预期的问题。
posted @ 2009-04-10 16:46  hongbo0521  阅读(2467)  评论(2编辑  收藏  举报