作者:草履虫
email:
[email protected]
原文出自:
cceer.xmu.edu.cn/blog/
---->什么是类和对象
这是所有面向对象编程之前必须弄明白的.
所谓类:简单地说就是模板,说的专业一些,是一类具有某种性质的物的集合.比如:人就是一个类,车也是一个类,等等.
所谓对象:就是类的具体实现.如上面所说,人是一个类,一个具体的人就是一个对象,比如张三.
对象是类的实例化后的结果.javascript中使用new关键字来实例化一个类,生成一个对象.
例子:
function people(){ //javascript中一个function也是一个类,这里我们建立一个空的类people
}
var zhangsan=new people; //实例化生成一个对象张三
---->一个具体形象的例子
/*-->最简单的类:
*people类
* 属性:性别,年龄,姓名
* 方法:说话
*/
function people(name,sex,age){
this.name=name;
this.sex=sex;
this.age=age;
this.say=function(){
return "我叫"+this.name;
}
}
使用时:
var zhangsan=new people;
alert(zhangsan.say());
var lisi=new people;
alert(lizi.say());
说明:
上面使用了this关键字,this总是指向当前的对象,在上面例子中,zhangsan.say中具有this.name,这里的this是当前的对象zhangsan.后面lisi.say则是指向当前对象lisi
对象具有属性,上面的name,sex和age就是对象的属性.我们这样可以访问,如lisi.name,zhangsan.age
对象还具有方法,比如上面的say.方法是通过构造函数实现的.使用时,如上面,用lisi.say(),zhangsan.say()
当然我们还可以在实例化对象后为对象添加新的属性和方法.比如:
zhangsan.girlfriend="小丽";
zhangsan.doing=function(){
return "I am eating";
}
---->javascript类/对象和其他面向对象语言的异同
相同点:面向对象编程的思想都是一样的,世界上所有的具体事物都可以看成对象,而这些事物从属的集合都可以看成类.我们要做的是构造我们需要的类,在实例化成我们需要的对象为我们工作.
不同点:其他面向对象编程的语言对于类/对象关心下面的事情:
1.作用域:公用,私用,受保护,静态.而javascript只有一种作用域:公用作用域.
2.特性:继承,多态.javascript不支持多态,继承方面的内容将在"javascript对象的继承"一文中介绍
---->构建javascript类/对象的方式
首先,可以大致定义出下面几种类型:
1.工厂方式
2.构造函数方式
3.原型方式
4.混合的构造函数/原型方式
5.动态原型方法
6.混合工厂方式
具体说明:
A.工厂方式:
所谓工厂方式,是指先建立对象,然后再往对象里面添加属性和方法.
eg.1
var zhangsan=new Object; //创建对象
zhangsan.name="张三"; //给对象添加属性
zhangsan.say=function(){ //给对象增加方法
alert("我叫张三");
}
eg.2 上面的例子没有封装性,我们可以使用函数封装,实现多重利用
function people(){
var p_object=new Object;
p_object.name="张三";
p_object.say=function(){
alert("我叫张三");
}
return p_object; //返回对象
}
var zhangsan=people;
var lisi=people;
上面zhangsan和lisi两个对象具有完全相同的属性和方法,都叫"张三"(name属性),都会说"我叫张三"(say()方法)
eg.3 通过传递参数改进eg.2
function people(name){
var p_object=new Object;
p_object.name=name;
p_object.say=function(){
alert("我叫"+this.name);
}
return p_object; //返回对象
}
var zhangsan=people("张三");
var lisi=people("李四");
总结:
工厂方式,总是先创建一个对象,再往对象中添加你需要的属性和方法.但这种做法对于封装性和多种利用性不是很有利,这导致了这种对象的构造方法不被提倡.
使用工厂方式总是为每个对象创建独立的函数版本.
这类方式使用封装然后调用新对象的时候不使用new创建对象.
B.构造函数方式:
所谓构造函数方式,就像我给出的例子"一个具体形象的例子",就是采用构造函数的方式.它和工厂方式的区别是不再在函数内部创建一个对象.而是通过this关键字指向当前对象.
构造函数的例子不再给出.
构造函数和工厂方式一样,会重复生成函数,为每个版本的对象创建独立的函数版本.
C.原型方式
所谓原型方式,就是利用prototype属性来实现属性和方法的继承
eg.1
function people(){
}
people.prototype.name="张三";
people.prototype.say=function(){
alert("我叫"+this.name);
};
var zhangsan=new people();
var lisi=new people();
原型方式不能通过构造函数传递参数初始化属性的值,因为所有的属性和方法都是通过prototype添加的
D.混合的构造函数/原型方式
对于对象的属性,使用构造函数的方式
对于对象的方法,使用原型方式
eg.1
function people(name){
this.name=name;
}
people.prototype.say=function(){
return "我的名字叫"+this.name;
};
var zhangsan=new people("张三");
document.write(zhangsan.say());
eg.2 我们也可以把prototype写入类,实现视觉上的封装.
function people(name){
this.name=name;
people.prototype.say=function(){
return "我的名字叫"+this.name;
};
}
var zhangsan=new people("张三");
document.write(zhangsan.say());
总结:这种构造类/对象的方法是推荐使用的
E.动态原型方式
这是在混合的构造函数/原型方式上改进的一种方式(提供更友好的编码风格),他们功能是等价的
eg.1
function people(name){
this.name=name;
if(typeof people._initialized=="undefined"){
people.prototype.say=function(){
return "我的名字叫"+this.name;
};
people._initialized=true;
}
}
var zhangsan=new people("张三");
document.write(zhangsan.say());
var lisi=new people("李四");
document.write(lisi.say());
这样处理的目的是创建对象的方法后下一次使用时不要再创建.
由于上面的原因,动态原型方式也是javascript中常用的一种创建类/对象的一种方式
F.混合工厂方式
混合工厂方式几乎和工厂方式是一样的.它同样是先构造对象,然后再往对象中添加属性和方法.不同的是,混合工厂方式生成对象时依旧使用new关键字.
eg.1
function people(){
var p_object=new Object;
p_object.name="张三";
p_object.say=function(){
alert("我叫张三");
}
return p_object; //返回对象
}
var zhangsan=new people;
var lisi=new people;
zhangsan.say();
lisi.say();
混合工厂方式和工厂方式以及经典方式(构造函数,原型方式)一样会产生问题,不推荐使用
对各种构建类/对象方式的总结:
通常地,我们使用混合的构造函数/原型方式,即属性使用构造函数方式,方法采用原型方式.当然,加强地,我们可以使用动态原型方式.
上面两种方式是推荐使用的.
---->关于prototype的其他功能
1.给对象(包括本地对象)添加新的方法
比如Array对象,你可能需要添加一个方法toHexString,你可以这样做:
Array.prototype.toHexString=function(){
//code here
}
2.重定义方法
实质是让方法指向一个新的函数
Array.prototype.toHexString=function(){
//other code href
}
对象继承
继承,通俗地说,之前你写过一些类,这些类中有一些是而你现在要写的类的功能的子集或者基本相同,那么你不用完全重新写一个新的类,你可以把之前写的类拿过来使用.这样的一种代码重用过程就叫做继承.
深入学习javascript继承之前,先了解下面的几个概念:
父类:被继承的类
子类:由继承得来的类
超类:也就是父类
抽象类:一般不用来实例化的类,它的用途是用来给其他类继承.
基类:提供给其他类可以继承的类
派生类:由基类继承而来的类
javascript对象继承通常有下面的5种方式:
1.对象冒充
2.call()方式
3.apply()方式
4.原型链
5.混合方式
A.对象冒充
所谓对象冒充,就是新的类冒充旧的类(旧的类必须采用构造函数方式),从而达到继承目的.
eg.1
function people(name,sex,age){ //使用构造函数方式
this.name=name;
this.sex=sex;
this.age=age;
this.say=function(){
alert("My name is "+this.name);
};
this.doing=function(){
alert("I am speaking");
};
}
var Marry=new people("Marry","Woman","23");
Marry.say();
Marry.doing();
function white_people(name,sex,age){
this.inherit=people;
this.inherit(name,sex,age);
delete this.inherit;
this.area=function(){
alert("I am in Europe");
}
}
var Tom=new white_people("Tom","man","21");
Tom.say();
Tom.area();
alert(Tom.age);
上面的例子中,people是用来做white_people的基类,记住这个格式是用来对象冒充达到继承目的的
this.inherit=people; //冒充
this.inherit(name,sex,age); //继承
delete this.inherit; //删除继承
所有新属性和新方法都必须再删除了继承后定义,这样是为了避免覆盖父类的相关属性和方法.
另外,对象冒充支持多继承.
eg.2
function worker(pay,work){
this.pay=pay;
this.work=work;
}
function city_worker(name,sex,age,pay,work){
this.inherit=people;
this.inherit(name,sex,age);
delete this.inherit;
this.inherit=worker;
this.inherit(pay,work);
delete this.inherit;
}
var Jerry=new city_worker("Jerry","man","21","$1000","coder");
Jerry.say();
alert(Jerry.work);
对象冒充有一个不足的地方:多继承机制实现时,如果基类存在相同的属性或者方法,将从后面的类继承.
B.call()方式
只是封装的对象冒充的一个函数.这样,我们不再需要写"经典"的三句话,而是用下面这句话代替:
基类.call(对象,参数列表)
eg.1
function farmer(name,sex,age,pay,work){
people.call(this,name,sex,age);
worker.call(this,pay,work);
}
var Nicholas=new farmer("Nicholas","man","27","$3000","irrigator");
Nicholas.say();
alert(Nicholas.pay);
同样,call()存在同名属性和方法的小问题.
C.apply()方式
和call()一样.apply()也是对象冒充的一个封装函数.其格式为:
基类.apply(对象,参数数组);
eg.1
function white_collar(name,sex,age,pay,work){
people.apply(this,new Array(name,sex,age));
worker.apply(this,[pay,work]);
}
var Jiessie=new white_collar("Jiessie","woman","26","$2500","editor");
Jiessie.say();
alert(Jiessie.work);
同样,apply()存在同名属性和方法的小问题.
D.原型链
上面三种方式都是采用构造函数方式的继承,对应地,也具有原型函数方式的继承:原型链.
eg.1
function blue_collar(){
}
blue_collar.prototype.name="Jean";
blue_collar.prototype.age="33";
blue_collar.prototype.say=function(){
alert("my name is "+ this.name);
};
function city_blue_collar(){
}
city_blue_collar.prototype=new blue_collar();
var jj=new city_blue_collar;
jj.say();
原型链也具有了原型链的缺点:不能传递参数.另外,原型链不支持多继承,因为
E.混合方式
使用构造函数方式来写类的属性,对属性的继承采用call()或者apply()
使用原型方式来写的方法,对方法的继承采用原型链
eg.1
function beauty(name,age){
this.name=name;
this.age=age;
}
beauty.prototype.say=function(){
alert("小女叫"+this.name);
};
function china_beauty(name,age,area){
beauty.call(this,name,age);
this.area=area;
}
china_beauty.prototype=new beauty();
china_beauty.prototype.from=function(){
alert("我来自"+this.area);
};
var diaochan=new china_beauty("貂禅","16","临洮");
diaochan.say();
diaochan.from();
alert(diaochan.age);
对象编程规范(个人)
前面两篇介绍了javascript对象编程的基础以及对象的继承.
在基础篇我们知道如何创建对象,并通过对象编程.当然,我们还必须知道各种创建对象的方法以及他们之间的利弊.
在对象继承篇我们知道了如何利用对象的继承性质提高代码的可重用性,以及如何根据创建对象方式选择对应的继承方式.
对于多种创建类/对象的方式,以及对应的创建继承方式,有必要根据自己的习惯,以及更好的编码习惯来规定一种规范.下面是针对自己的javascript创建类/对象的规范.
1.如何创建类/对象
使用"混合构造函数/原型方式"
2.怎么继承
对应创建,采用"混合方式"进行继承
采用之前写的一个例子.
function beauty(name,age){ //基类:美女,这里采用构造函数方式,接收参数,设置类的属性
this.name=name; //美女的名字
this.age=age; //美女的年龄
}
beauty.prototype.say=function(){ //基类:为美女添加方法,采用原型方式
alert("小女叫"+this.name);
};
function china_beauty(name,age,area){ //派生类:中国美女
beauty.call(this,name,age); //继承美女的属性
this.area=area; //添加自己的属性:来源地
}
china_beauty.prototype=new beauty(); //继承美女的方法
china_beauty.prototype.from=function(){ //添加自己的方法:介绍来源地
alert("我来自"+this.area);
};
var diaochan=new china_beauty("貂禅","16","临洮"); //创建中国美女对象"貂禅"
diaochan.say(); //使用继承自美女类的方法say
diaochan.from(); //使用自己的方法from
alert(diaochan.age); //使用继承自美女的属性age