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新闻:
» 更多新闻...
最新知识库文章:
======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/