您的位置:首页 > Web前端 > JavaScript

演示OOP中的封装、继承在JavaScript中的书写方式(另附JS反射的例子)

2009-04-25 22:23 821 查看
演示OOP中的封装、继承在JavaScript中的书写方式(另附JS反射的例子)

最近几天都在尝试去理解学习老师演示的JS面向对象编程的例子,我细读这些例子之后,也终于找到了JS中运用Java中已掌握的面向对象编程方式去写JS代码的感觉了。于是,在老紫竹的世纪网上我也写了几篇帖子以演示最近的学习所得。

下面是这几天写的几篇帖子,整理一下给大家分享一下,希望过路的朋友能指正其中的错误,以帮助我更好地理解JS的OO思想。

一、演示OOP中的封装在JavaScript中的书写方式


在Java中书写Pojo已经是一件司空见惯的事情,当然,也是一件可以称之为“体力活”的事情。

今天,初初学习JS中的OOP,既然面向对象的三大特性是:封装、继承、多态,那么按照这个次序,
我便先弄弄JS中对象的封装。虽然是体力活,但还是需要去做。

以下便是我使用Java的Pojo书写方式来写JS的Pojo:

运行效果:



源码清单:

/**
* 学员Pojo
* 构造方式1:var 变量名 = new StudentPojo();
* 构造方式2:var 变量名 = new StudentPojo(学员ID, 学员姓名, 学员年龄);
*/
var StudentPojo = function() {

var paramsLength = arguments.length;  // 可变参数
var studentId = 0;    // 学员ID
var studentName = "";  // 学员姓名
var studentAge = 18;   // 学员年龄

// 处理构造器参数(参数长度与成员变量个数相同时作为参数化构造器处理)
if (paramsLength == 3) {
studentId = arguments[0];
studentName = arguments[1];
studentAge = arguments[2];
}

// 学员ID取值方法
this.getStudentId = function() {
return studentId;
}
// 学员ID赋值方法
this.setStudentId = function(id) {
studentId = id;
}
// 学员姓名取值方法
this.getStudentName = function() {
return studentName;
}
// 学员姓名赋值方法
this.setStudentName = function(name) {
studentName = name;
}
// 学员年龄取值方法
this.getStudentAge = function() {
return studentAge;
}
// 学员年龄赋值方法
this.setStudentAge = function(age) {
studentAge = age;
}

// 获取内容的toString方法
this.toString = function() {
return "学员ID = " + studentId
+ ",学员姓名 = " + studentName
+ ",学员年龄 = " + studentAge;
}

}

/**
* 程序入口方法
*/
var main = function() {

var stuPojo1 = new StudentPojo(1, "张三", 25);
var stuPojo2 = new StudentPojo(2, "李四", 19);
var stuPojo3 = new StudentPojo(3, "王五", 17);
var stuPojo4 = new StudentPojo(4, "赵六", 22);
var stuPojo5 = new StudentPojo(5, "周七", 23);
var stuPojo6 = new StudentPojo(6, "陈八", 20);
var stuPojoArr = [stuPojo1, stuPojo2, stuPojo3, stuPojo4, stuPojo5, stuPojo6]
var table = "";  // 保存<table>标签构建内容

// 构建table内容
table += "<table cellpadding=/"0/" cellspaceing=/"0/">";
table += "<tr>";
table += "<th>学员ID</th>";
table += "<th>学员姓名</th>";
table += "<th>学员年龄</th>";
table += "</tr>";

for (var i = 0; i < stuPojoArr.length; i++) {
table += "<tr>";
table += "<td>" + stuPojoArr[i].getStudentId() + "</td>";
table += "<td>" + stuPojoArr[i].getStudentName() + "</td>";
table += "<td>" + stuPojoArr[i].getStudentAge() + "</td>";
table += "</tr>";
}

table += "</table>";

// 显示table内容
document.getElementById("content").innerHTML = table;

var print = "";  // 保存打印Pojo的toString方法内容

// 构建toString方法拼接内容
for (var i = 0; i < stuPojoArr.length; i++) {
print += stuPojoArr[i].toString() + "<br />";
}

// 显示print内容
document.getElementById("print").innerHTML = print;

}


以上仅为个人学习JS面向对象编程的一个开端,如果有什么不正确的地方,还烦请各位指正为感!

By CodingMouse
2009年4月23日

二、演示JS中动态调用类中的方法(JS的反射机制)

原本计划是紧接着上一篇文章《演示OOP中的封装在JavaScript中的书写方式》之后 继续写JS的类继承,结果因为要在项目中将Ajax回调返回的XML自动映射为JavaScript的Pojo类(其中,Ajax的XML内容也是由Java的Pojo类自动映射生成的),但却因为无法动态调用JS的类方法而苦恼(在上篇文章中可以看到,调用JS类方法是使用点“.”操作符显式调用的,这样就必须知道类的方法名)。其实语言之间都有互通性,那么,我们能不能在JS中找到类似于Java反射的机制呢?答案是肯定的。我们将要用到平时我们书写普通的表单验证中很少见的eval()方法(在JS领域更贴切的说法是“函数”)。下面是在w3School在线教程上的JScript手册中查到的API:
JavaScript eval() 函数

定义和用法
eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。

语法
eval(string)参数 描述
string 必需。要计算的字符串,其中含有要计算的 JavaScript 表达式或要执行的语句。

返回值
通过计算 string 得到的值(如果有的话)。

说明
该方法只接受原始字符串作为参数,如果 string 参数不是原始字符串,那么该方法将不作任何改变地返回。因此请不要为 eval() 函数传递 String 对象来作为参数。

如果试图覆盖 eval 属性或把 eval() 方法赋予另一个属性,并通过该属性调用它,则 ECMAScript 实现允许抛出一个 EvalError 异常。

抛出
如果参数中没有合法的表达式和语句,则抛出 SyntaxError 异常。

如果非法调用 eval(),则抛出 EvalError 异常。

如果传递给 eval() 的 Javascript 代码生成了一个异常,eval() 将把该异常传递给调用者。
提示和注释
提示:虽然 eval() 的功能非常强大,但在实际使用中用到它的情况并不多。
实例
例子 1
在本例中,我们将在几个字符串上运用 eval(),并看看返回的结果:

<script type="text/javascript">

eval("x=10;y=20;document.write(x*y)")

document.write(eval("2+2"))

var x=10
document.write(eval(x+17))

</script>输出:

200
4
27例子 2
看一下在其他情况中,eval() 返回的结果:

eval("2+3") // 返回 5
var myeval = eval; // 可能会抛出 EvalError 异常
myeval("2+3"); // 可能会抛出 EvalError 异常
可以使用下面这段代码来检测 eval() 的参数是否合法:

try {
alert("Result:" + eval(prompt("Enter an expression:","")));
}

catch(exception) {
alert(exception);
}

以上内容引用地址:JavaScript eval() 函数

w3School提供了在线的eval()函数测试工具,地址为:eval()函数在线测试工具

通过上面的API和在线测试可以看得出来,eval()函数的这种动态特性正好与Java的反射机制不谋而合(当然,Java也有抄JS这种动态语言特性的嫌疑,至于到底谁抄谁的我也没去详细了解了,反正这种动态特性可以让我们写出更加灵活的程序)。

资料也查到了,也在线测试了,那么,让我们一起来完成本文的主题——JS动态调用类中的方法。

...

由于只是一个演示,所以我就不再将JS文件与HTML文件分离开来了,而且本次演示的主角也是JS。
以下是我完成的一段演示代码清单:

第一种方式(不使用eval()函数):
<html>
<head>
<title>JavaScript Reflect Demo By CodingMouse</title>
<mce:script type="text/javascript"><!--

/**
* 学员Pojo类
* 演示JS中动态调用类中的方法(JS的反射机制)
* By CodingMouse
*/
var StudentPojo = function(){

// 定义私有域(即成员变量)
var name ;   // 学员姓名
var age  ;   // 学员年龄
var sex  ;   // 学员性别

// 学员姓名取值方法
this.getName = function() {
return name;
};
// 学员姓名赋值方法
this.setName = function(n) {
name = n;
};
// 学员年龄取值方法
this.getAge = function () {
return age;
};
// 学员年龄赋值方法
this.setAge = function(a) {
age = a;
};
// 学员性别取值方法
this.getSex = function() {
return sex;
};
// 学员性别赋值方法
this.setSex = function(s) {
sex = s;
};

};

// 测试程序主方法
function main(){

var stu = new StudentPojo();
stu.setName("CodingMouse");
stu.setAge(20);
stu.setSex("男");

// 遍历Pojo成员
for(var field in stu){

// 筛选出方法
if(typeof stu[field] == "function"){
// 筛选出getter方法
if(field.substring(0,3) == "get"){
// 显示getter内容
alert("方法名:" + field + ",值:" + stu[field]());
}
}

}

};

// --></mce:script>
</head>
<body onload="main();">
<h4>演示JS中动态调用类中的方法(JS的反射机制) By CodingMouse</h4>
</body>
</html>

第二种方式(使用eval()函数动态执行类方法):

<html>
<head>
<title>JavaScriptReflectDemo - using eval function By CodingMouse</title>
<mce:script type="text/javascript"><!--

/**
* 学员Pojo类
* 演示JS中动态调用类中的方法(JS的反射机制)- 使用 eval() 函数
* By CodingMouse
*/
var StudentPojo = function(){

// 定义私有域(即成员变量)
var name ;   // 学员姓名
var age  ;   // 学员年龄
var sex  ;   // 学员性别

// 学员姓名取值方法
this.getName = function() {
return name;
};
// 学员姓名赋值方法
this.setName = function(n) {
name = n;
};
// 学员年龄取值方法
this.getAge = function () {
return age;
};
// 学员年龄赋值方法
this.setAge = function(a) {
age = a;
};
// 学员性别取值方法
this.getSex = function() {
return sex;
};
// 学员性别赋值方法
this.setSex = function(s) {
sex = s;
};

};

// 测试程序主方法
function main(){

var stu = new StudentPojo();
stu.setName("CodingMouse");
stu.setAge(20);
stu.setSex("男");

// 遍历Pojo成员
for(var field in stu){

// 筛选出方法
if(typeof stu[field] == "function"){
// 筛选出getter方法
if(field.substring(0,3) == "get"){
// 显示getter内容
alert("方法名:" + field + ",值:" + eval("stu." + field + "()"));  // 就只有这一点点区别!
}
}

}

};

// --></mce:script>
</head>
<body onload="main();">
<h4>演示JS中动态调用类中的方法(JS的反射机制) By CodingMouse</h4>
</body>
</html>

呵呵!是不是很有乐趣?将Java中的许多OO思想搬抄过来可以让你的的JS写得更加优美!

附件中依然也提供了这段简单的演示代码。欢迎批评指正!

By CodingMouse
2009年4月23日
三、演示OOP中的继承在JavaScript中的书写方式


在我写的前面两篇文章《演示OOP中的封装在JavaScript中的书写方式》以及《演示JS中动态调用类中的方法(JS的反射机制)》中,我已经为大家演示了使用Java的OO编程思想来学习JavaScript的面向对象编程中关于继承反射的书写方式。如果你已经掌握了Java的面向对象基础知识,那么,阅读我写的这几篇关于JavaScript的面向对象编程演示文章会感到特别轻松,而且也会觉得什么JavaScript的闭包(Closure)也就是那么回事(对于JS中的闭包,较为直观的解释是:当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包)。由于我这里的演示只是以Java的思考方式来学习JS面向对象编程,所以,不在这里过多阐述JS的概念。不过请记住一点,要实现域的作用范围控制,就需要合理地使用闭包。

也许你在其它网文上见到过一些演示JS继承的例子,但那些例子大多数是将类的所有公共方法(域)都使用原型(prototype)来构建。那么,作为习惯了Java的OO思维方式的你是比较难以接受的。那么,我的例子又有什么特点呢?让我们一起来看看下面这段例程:
<html>
<head>
<title>JavaScript Inheritance Demo By CodingMouse</title>
<mce:script type="text/javascript"><!--

/**
* 人Pojo类(作为本演示中的基类(或称父类)角色出场)
* 演示OOP在JavaScript中继承的书写方式
* By CodingMouse
*/
var PersonPojo = function(){

// 定义私有域(即成员变量)
var name = "佚名" ;   // 姓名
var age  =   18   ;   // 年龄
var sex  =  "男"  ;   // 性别

// 姓名取值方法
this.getName = function() {
return name;
};
// 姓名赋值方法
this.setName = function(n) {
name = n;
};
// 年龄取值方法
this.getAge = function () {
return age;
};
// 年龄赋值方法
this.setAge = function(a) {
age = a;
};
// 性别取值方法
this.getSex = function() {
return sex;
};
// 性别赋值方法
this.setSex = function(s) {
sex = s;
};

}

/**
* 学员Pojo类(作为本演示中的派生类(或称子类)角色出场)
* 演示OOP在JavaScript中继承的书写方式
* By CodingMouse
*/
var StudentPojo = function(){

// 定义私有域(即成员变量)
var chineseScore = 0;  // 语文成绩
var mathScore    = 0;  // 数学成绩
var englishScore = 0;  // 英语成绩

// 语文成绩取值方法
this.getChineseScore = function() {
return chineseScore;
};
// 语文成绩赋值方法
this.setChineseScore = function(c) {
chineseScore = c;
};
// 数学成绩取值方法
this.getMathScore = function() {
return mathScore;
};
// 数学成绩赋值方法
this.setMathScore = function(m) {
mathScore = m;
};
// 英语成绩取值方法
this.getEnglishScore = function() {
return englishScore;
};
// 英语成绩赋值方法
this.setEnglishScore = function(e) {
englishScore = e;
};

};

/**
* 关键点:使用StudentPojo原型(prototype)方式呼叫PersonPojo出场,
* 通过指定prototype,你就可以在StudentPojo类中如同在Java中呼叫父
* 类的方式调用父类的公共域。此处可以理解为如同在Java中使用extends
* 关键字通知编译器StudentPojo类要继承PersonPojo类。
*/
StudentPojo.prototype = new PersonPojo();

// 测试程序主方法
function main(){

var stu = new StudentPojo();
stu.setName("CodingMouse");
stu.setAge(20);
stu.setSex("男");
stu.setChineseScore(95);
stu.setMathScore(86);
stu.setEnglishScore(77);

// 遍历Pojo成员
for(var field in stu){

// 筛选出方法
if(typeof stu[field] == "function"){
// 筛选出getter方法
if(field.substring(0,3) == "get"){
// 显示getter内容
alert("方法名:" + field + ",值:" + stu[field]());
}
}

}

};

// --></mce:script>
</head>
<body onload="main();">
<h4>演示OOP在JavaScript中继承的书写方式 By CodingMouse</h4>
</body>
</html>

呵呵!怎么样?是不是感觉很像Java的书写方式?

以上这段代码虽然还是使用的相对简单的原型(prototype)继承方式,但你可以看到,整个演示过程中prototype关键字只在代码中(注释除外)出现了一次,即指定其父类(PersonPojo)那一句:StudentPojo.prototype = new PersonPojo();,代码中我已经添加了详细的注释说明。

实质上,还有使用JS中的call方法和apply方法来实现的经典继承方式,由于这段时间很忙,且这种实现方式的编写要相对复杂些,我还得抽时间消化一下再给大家作演示。

By CodingMouse
2009年4月25日
以上是近段时间学习JS面向对象编程的一些点滴,在看到众多书写方式的情况下,寻找和尝试一种适合自己的实现方式才是对自己有益的。看到别人写的东西,如果不自己不能在别人已有的基础上进行优化和改良,那么也就失去了学习的意义。特别是上面对于JS继承的书写方式,我在其它地方暂时还没有看到过(也许只是我没有看到过)。
总之觉得使用Java里面这种较纯净的面向对象的书写方式来学习JS是一种非常好的途径。虽然老师在教学过程中提供一些实例让学员们去理解,但最终还是有相当部分的学员表现出诸多的反对和不理解(譬如上面那种Pojo的书写方式,实质上是为了控制域的作用范围,增强域的访问安全性,所有的私有域都必须通过其自身的公共方法进行修改,也就是所谓的“对象自治”,这一点与Java中的思想是一致的,只是JS中是通过闭包来规范域的作用范围)。实质上只是因为他们仅仅是希望少写代码来完成更多的事,却没有考虑到这样的OO实现方式所带来的价值和意义。在这里也很庆幸现在自己有这么一位好老师能帮助我理清学习的路线,让我少走很多弯路,在此对老师表示感谢!
我老师的博客:arkliszeng


By CodingMouse
2009年4月25日
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: