面向对象的 JavaScript 代码
本篇文章节选自:http://lijing.9966.org/jayli/yshare/p1/code.html 原文作者:jay
JavaScript是基于原型的编程语言,但也有完整的面向对象特性。同时其书写风格灵活多变,包括函数式编程、原型风格编程和面向对象风格编程。
从代码风格来看,封装类型分为类式的封装和原型式的封装。
这里我们使用“人”(Person)的例子,构造一个“人”的类,人有名字,人可以“吃饭”、“睡觉”、“走路”等等
1,类式封装
最直接最简单的封装方法是将人名,吃饭,睡觉等属性和动作封装在Person类中.
<script>
/*定义一个"人"的类,人有名字,人也可以吃饭,睡觉,走路
*我们用类式封装来实现
*但是所有成员都是公有的,任何人都能够在外部访问name,eat,sleep,walk
*///Person类
var Person = function(name){//公有成员
this.name = name;//公有方法
this.eat = function(){
alert(this.name+’ is eating sth’);
};
this.sleep = function(){
alert(this.name+’ is sleeping’);
};
this.walk = function(){
alert(this.name+’ is walking’);
};
};var jay = new Person(’jay’);
</script>
可以通过firebug看到“jay”的成员和属性。
但这样的代码有一个问题:不安全,因为类成员在任意命名空间都可以访问到。我们需要做改进:增加私有成员和私有方法。
通常约定私有成员变量用"_"开始,比如_value表示这是一个私有变量。
<script>
/*定义一个"人"的类,人有名字,人也可以吃饭,睡觉,走路
*针对类式封装,增加私有成员,私有方法
*这里增加的_setNickName和_getNickName只是说明私有方法的用法,实际中可以完全省略
*/
var Person = function(name){
//私有成员
var _nickname = 'nothing';
//公有成员
this.name = name;
//私有方法
var _setNickName = function(nickname){
_nickname = nickname;
};
var _getNickName = function(){
return _nickname;
};
//公共方法
this.eat = function(){
alert(this.name+' is eating sth');
};
this.sleep = function(){
alert(this.name+' is sleeping');
};
this.walk = function(){
alert(this.name+' is walking');
};
this.setNickName = function(nickname){//接口
_setNickName(nickname);
};
this.getNickName = function(){//接口
return _getNickName();
};
};
var jay = new Person('jay');
</script>
这样就可以通过Person提供的接口对私有成员进行访问。
类式封装的优点:思路很清晰,适合那些有c++或java编程经验的人。
类式封装的缺点:内存利用不科学,因为每个实例都拥有各自的成员方法,而各自的方法是一样的,我们希望每个实例都共享一个方法。而成员受运行时影响比较大,则希望每个实例的成员可以占据单独的内存。原型式封装弥补了这个缺陷,原型使得每个实例都共享一份原型方法。
2,原型式封装
<script>
/*定义一个"人"的类,人有名字,人也可以吃饭,睡觉,走路
*
*原型式封装,每个实例都共享一份原型,(jjb.eat == jay.eat) = true
*/
var Person = function(name){
var _nickname = 'nothing';
this.name = name;
this.setNickName = function(nickname){//接口方法
_nickname = nickname;
};
this.getNickName = function(){//接口方法
return _nickname;
};
};
//原型
Person.prototype = {
eat:function(){
alert(this.name+' is eating sth');
},
sleep:function(){
alert(this.name+' is sleeping');
},
walk:function(){
alert(this.name+' is walking');
}
};
var jay = new Person('jay');
var jjb = new Person('jjb');
</script>
这样jjb.eat == jay.eat,说明实例的eat是到Person的prototype.eat的引用.
原型式封装的优点:内存利用合理,缺点:初学难理解。
这种编码风格要注意的:1,方法在原型中定义,成员在类中定义,2,私有变量和接口函数写在类定义中,因为接口通常使用到私有变量。
3,静态变量
在C++,JAVA和C#中有类静态变量,如何在js中实现?
<script>
/*
* 给“人”中定义一个静态变量“人数”,这样每个“人”的实例都共享一个“人”的名为“人数”的成员
*/
var Person = (function(){
//私有静态成员
var numOfPersons = 0;
return function(name){
//公有成员
this.name = name;
//私有成员
var _nickname = 'nothing';
//接口方法
this.setNickName = function(nickname){
_nickname = nickname;
};
this.getNickName = function(){
return _nickname;
};
this.setNum = function(num){
numOfPersons = num;
};
this.getNum = function(){
return numOfPersons;
};
};
})();
//原型
Person.prototype = {
eat:function(){
alert(this.name+' is eating sth');
},
sleep:function(){
alert(this.name+' is sleeping');
},
walk:function(){
alert(this.name+' is walking');
}
};
var jay = new Person('jay');
var jjb = new Person('jjb');
</script>
在这个例子中,jjb.getNum() = 0;jjb.setNum(3);jay.getNum() = 3;