JavaScript面向对象编程(2)-- 类的定义

javascript面向对象编程(2)-- 类的定义

2008-05-29 11:05

by

johnson2008,

6196

visits,

收藏,

编辑

最近这一段时间事情太多了,没有时间再继续写,幸好这两天有点小闲,先小写一下javascript中面向对象一中推荐的方法。本文承接上一篇javascript面向对象编程(1) -- 基础

上篇说过,javascript没有类的概念,需要通过函数来实现类的定义。先通过一个例子说明:

function myclass()

{

var id = 1;

var name = "johnson";

//properties

this.id = id;

this.name = name;

//method

this.showmessage = function()

{

alert("id: " + this.id + ", name: " + this.name);

}

}

var obj1 = new myclass();

var obj2 = new myclass();

function的定义实际上相当于类的构造函数,最后两句是创建这个类的实例。先分析第一句:var obj1 = new myclass(); 当用new创建类的实例时,解释器首先会创建一个空的对象。然后运行这个myclass函数,并将this指针指向这个类的实例。当碰到this.id = id;和this.name = name;及this.showmessage = function(){...}时,便会创建这两个属性,和这个方法,并把变量id,name的值一级函数的定义赋给这两个属性及这个函数对象(shwomessage)。这个过程相当于初始化这个对象,类似于c# 中的构造函数。最后new返回这个对象。再看第二句:var obj2 = new myclass(); 执行过程与上一句代码相同,即创建一个空对象,然后执行myclass这个函数,定义两个属性和一个方法。

从上面的分析中可以看到,上面这种实现类的方式,即在函数的定义中定义类的属性方法。存在着弊端。如果需要创建两个或更多这个类的实例时,上文是两个,这些属性会被重复的创建多次。

那么如何避免这种情况呢?上一篇中也曾提到过用prototype。prototype和它的名字一样是一个原型,每一个function都有一个子对象prototype,它其实表示这个function对象的成员的集合,由于这里我们使用function实现类的,所以可以说prototype其实就是便是类的成员的集合。prototype定义的属性和方法执行在函数的构造体执行之前,所以当new一个对象之前,其实prototype的成员已经执行过了。先看一个例子:

function myclass()

{

//构造函数

}

myclass.prototype =

{

id: 1,

name: "johnson",

showmessage: function()

{

alert("id: " + this.id + ", name: " + this.name);

}

}

var obj1 = new myclass();

var obj2 = new myclass();

类的结构还是和前面的例子相同,只不过这里是利用了prototype来实现。还是先看最后两句,前面说过,prototype是执行在函数构造体之前,即执行到var obj1 = new myclass();之前,这个类已经有了id,name属性和showmessage方法。执行者一句时执行过程如下,注意和前一个例子比较:首先还是创建一个空的对象,并把this指针指向这个对象。然后将函数的prototype对象的所有成员都赋给这个对象(注意没有再创建这些成员)。然后执行函数体。最后new返回这个对象。执行下一句时:同样执行此过程,不会重复创建这些成员。

上面的代码还只是一个例子,在实际的项目中,可能出现的是类中有大量的成员,同时可能需要创建大量的实例。这是prototype就会显示其优越性了。另外上面的代码中使用了大括号语法定义了prototype的成员,这样看起来代码更清晰。这是一种比较推荐的类的设计模式。当然在众多的项目中,可能还会发现更好的模式,我们也希望能有更优化的javascript的编程模式不断推陈出新,也希望随着时间的推移,各主流浏览器也对javascript的解析都标准,统一。

上面说过prototype定义的成员是发生在构造体之前,可以证明一下,在上面的例子中,构造体是空的,在构造函数中加入一句alert(this.name);,当执行到var obj1 = new myclass();时,会看到弹出对话框,显示正确的属性值。

写了这段文字之后承蒙多为兄弟的点评,收获匪浅。对上面的例子进一步讨论,如下代码:

function subclass(){ }

subclass.prototype =

{

name: "sub"

}

function myclass()

{

//构造函数

}

myclass.prototype =

{

id: 1,

name: "johnson",

subobj: new subclass(),

showmessage: function()

{

alert("id: " + this.id + ", name: " + this.name + "subobj.name:" + this.subobj.name);

}

}

var obj1 = new myclass();

obj1.subobj.name = "xxx";

obj1.showmessage();

var obj2 = new myclass();

obj2.showmessage();

这里在myclass中定义了一个引用类型,其类型是我们自定义的一个subclass类,这个子类中有一个name属性。由于prototype对象是共享的,按照我们上面的分析:在执行var obj1 = new myclass();时,会把myclass的prototype中的成员复制给这个obj1实例。但这里subobj是一个引用类型,在执行到var obj2 = new myclass();时,prototype中的id,name成员会复制到obj2中,但subobj这个属性不会复制过去,而是引用了prototype中的subobj,所以因为上一句修改了obj1.subobj.name的值,所以在用new生成obj2实例时,引用到了修改后的值。

所以借用prototype定义类时,依然需要将属性定义在构造体中,而将方法定义在该构造体的原型上。如下:

function myclass(id, name)

{

this.id = id;

this.name = name;

}

myclass.prototype =

{

showmessage: function()

{

alert("id: " + this.id + ", name: " + this.name);

},

showmessage2: function()

{

alert("method2");

}

}

var obj1 = new myclass(1, "johnson");

obj1.showmessage();

obj1.name="john";

obj1.showmessage();

var obj2 = new myclass(2, "amanda");

obj2.showmessage();

关于私有成员,共有成员以及静态成员,类的继承,抽象类,虚方法,类的反射等实现方法,以后还会坚持写下去。不过我觉得需要说一下的是,我打算写的是javascript面向对象的基础实现,如果需要深入的学习建议参考李战老哥的“甘露模型”。

绿色通道:好文要顶关注我收藏该文与我联系

add your comment

17 条回复

1252149

#1楼 李战 2008-05-29 12:32

要支持俺们阵营里的哥们儿。

prototype里定义的东西都是只读共享的,因此比较适合在其上定义类的方法,而不适合定义属性。因为此类的对象可以读到这个共享的属性,但只要一写这个属性就产生了对象自己的属性副本。

回复 引用 查看

#2楼[楼主] johnson2008 2008-05-29 12:52

@李战

同意,是的,prototype定义的成员形式上是共享的。但是这里定义的是一个类,这里用prototype定义的属性的值就是这些属性的默认值。在建立类的实例时,就是要把这些共享的属性复制给每一个实例,使其成为特定实例的属性。对象可以动态地增加属性,但由于javascript没有实现面向对象的内在机制,如果要比较规则地实现类的定义,这种方法是一种规范的约束。

以上是我的观点,如有疏忽或不妥,欢迎赐教。

回复 引用 查看

#3楼 痴情客 2008-05-29 16:01

支持楼主

不过往往是两者结合使用的。

回复 引用 查看

#4楼 念时 2008-05-29 16:51

function myclass()

{

//构造函数

}

myclass.prototype =

{

id: 1,

name: "johnson",

showmessage: function()

{

alert("id: " + this.id + ", name: " + this.name);

}

}

var obj1 = new myclass();

var obj2 = new myclass();

这样仍然会创建两个对象啊,您说的“执行下一句时:同样执行此过程,不会重复创建这些成员”是什么意思呢。

我如果:

var obj1 = new myclass();

obj1.id=300;

obj1.name="wsy";

obj1.showmessage(); 弹出“300,wsy”

var obj2 = new myclass();

obj2.showmessage();弹出“1,johnson”

两个对象互不干扰啊???

回复 引用 查看

#5楼 念时 2008-05-29 17:10

是不是用了prototype之后 ,showmessage()方法成员不会被重复创建呢?

回复 引用 查看

#6楼 怪怪 2008-05-29 17:32

你这个的好处是重用了new操作符, 不过要说再省俩内存, 还是李战老哥的方法好。 我是指定义类的方法。

至于属性, 如果类比.net, 你这个其实算不上属性, 因为没有独立的getter和setter; 不过有一个好处, 等于有了默认值。

回复 引用 查看

#7楼 化石[未注册用户]2008-05-29 17:48

楼主接下来就要给大家讲讲类继承了:-)

回复 引用

#8楼[楼主] johnson2008 2008-05-29 17:50

每一个对象应该有各自不同的属性的副本,这里用prototype是在用new操作符建立多个实例时节省了重复创建prototype中的成员的重复创建问题,不是从内存的角度出发的。当用new建立对象时,执行的是成员的拷贝。因为javascript没有内在专门的机制实现面向对象,所以属性不能用get或set的有无来判断吧。不过赞同您的看法,如果能用get和set实现,会更好。学习了,谢谢。

回复 引用 查看

#9楼[楼主] johnson2008 2008-05-29 17:52

@念时

您的代码是正确的,多个对象得到了自己的属性。

回复 引用 查看

#10楼 化石[未注册用户]2008-05-29 17:59

如果属性是值类型的话,那么不同实例确实会有各自的副本,但是如果属性是一个引用类型的话,情况就不一样了:

function myclass()

{

//构造函数

}

myclass.prototype =

{

pro : {},

showmessage: function()

{

alert(this.pro.msg);

}

}

var obj1 = new myclass();

obj1.pro.msg = "msg1";

var obj2 = new myclass();

obj1.showmessage();

obj2.showmessage();

回复 引用

#11楼 疯子阿飞 2008-05-29 18:24

jscript.net(又名jscript8) 中是由类的概念的。

jscript8中可以产生和java一样的架构,例如:

import system;

package mainapp{

class page{

function page(){

var res:string="这是一个构造函数";

}

}

}

回复 引用 查看

#12楼 q.yuhen[未注册用户]2008-05-29 20:24

@化石 [未注册用户]

我认同化石的说法。从面向对象的角度来说,属性或者说字段属于对象实例的特征,放到类(prototype)里面共享有些奇怪,更何况这里面还有引用类型属性的问题。而方法通常属于群体特征行为,放到 prototype 里面就是应该的了。

我更习惯将相关成员放到构造函数内部,看上去更整洁一些。

function myclass()

{

this.id = 1;

this.name = "johnson";

if (typeof(this.showmessage) == "undefined")

{

this.constructor.prototype.showmessage = function()

{

alert("id: " + this.id + ", name: " + this.name);

};

// ... more function ...

}

}

var a = new myclass();

a.id = 1;

a.name = "tom";

a.showmessage();

var b = new myclass();

b.showmessage();

// 判断是否指向同一个函数实例

document.write(a.showmessage == b.showmessage);

回复 引用

#13楼 q.yuhen[未注册用户]2008-05-29 20:30

从面向对象的角度来说,属性或者说字段属于对象实例的特征,...

我这句话有点问题,"属性或者说字段" 是指属性或字段的值,而非成员本身。

回复 引用

#14楼 怪怪 2008-05-29 23:36

@johnson2008

呵呵, 是我说远了, 不过你可以去看看李战老哥的那篇文章, 就明白我说的什么意思了, 主要你还没开始说到继承, 所以我说的他那个再多省俩内存的好处还体现不出来。

回复 引用 查看

#15楼[楼主] johnson2008 2008-05-31 09:33

今天看了李战老哥的那个文章,感觉真是高啊!把javascript演示的出神入化了。收益匪浅哪。

回复 引用 查看

#16楼 kroda666[未注册用户]2008-07-08 13:32

请教一下js中面向对象的用法

/*错误

function myclass()

{

//构造函数

var name="zzz";

var id="aaa"

}

var obj=new myclass();

alert(obj.name) //为何提示undefined

//第二个

function myclass(){

var aa="aa";

var bb=1;

}

myclass.prototype.show=function(){

//var a=new myclass();

//alert(a.aa)

alert(aa);

alert(this.aa);

}

new myclass().show(); //同样的提示我没有声明。。。

*/

不太明白为什么会这样

回复 引用

#17楼[楼主] johnson2008 2008-07-12 01:24

@kroda666

首先,感谢您这么久了还留意了这个随笔,最近刚到新的单位,实在太忙了,没有精力继续往下写。等稳定下来,会继续写一些内容。

第一个

你用var 声明的变量相当于这个类的私有变量,在外部自然访问不到了,如果要声明公共的属性,需要用this获得对该对象的引用。如下

function myclass()

{

//构造函数

this.name="zzz";

this.id="aaa"

}

var obj=new myclass();

alert(obj.name);

第二个

是因为prototype的初始化工作发生在函数体执行之前。我在第一篇中提到过这个问题。http://www.cnblogs.com/johnson2008/archive/2008/05/15/1198252.html,如果你愿意,可以看一下。

回复 引用 查看

注册用户登录后才能发表评论,请 登录 或 注册,返回博客园首页

首页博问闪存新闻园子招聘知识库

最新it新闻:

·小米晒余额:支付宝昨日到账1.22亿

·windows 7官方rss动态主题:《昆虫》

·捡到iphone 4s玩自拍 icloud同步酿悲剧

·铁道部购票网站存泄密危险 cdn服务商技术短板是主因

·利用 mimo magictouch 打造另类的平板电脑

» 更多新闻...

最新知识库文章:

·设计师的品牌意识

·如何成为“10倍效率”开发者

·快速排序(quicksort)的javascript实现

·wcf服务端运行时架构体系详解[续篇]

·wcf服务端运行时架构体系详解[下篇]

» 更多知识库文章...

china-pub 2011秋季教材巡展

china-pub 计算机绝版图书按需印刷服务


======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值