JavaScript中支持面向对象的基础.docx
《JavaScript中支持面向对象的基础.docx》由会员分享,可在线阅读,更多相关《JavaScript中支持面向对象的基础.docx(45页珍藏版)》请在冰点文库上搜索。
![JavaScript中支持面向对象的基础.docx](https://file1.bingdoc.com/fileroot1/2023-7/14/46bde815-993c-4c5f-a82a-33aab3c61bb3/46bde815-993c-4c5f-a82a-33aab3c61bb31.gif)
JavaScript中支持面向对象的基础
6.1函数定义
6.1.1用定义函数的方式定义类
在面向对象的思想中,最核心的概念之一就是类。
一个类表示了具有相似性质的一类事物的抽象,通过实例化一个类,可以获得属于该类的一个实例,即对象。
在JavaScript中定义一个类的方法如下:
functionclass1(){
//类成员的定义及构造函数
}
这里class1既是一个函数也是一个类。
可以将它理解为类的构造函数,负责初始化工作。
6.1.2 使用new操作符获得一个类的实例
在前面介绍基本对象时,已经用过new操作符,例如:
newDate();
表示创建一个日期对象,而Date就是表示日期的类,只是这个类是由JavaScript内部提供的,而不是由用户定义的。
new操作符不仅对内部类有效,对用户定义的类也同样有效,对于上节定义的class1,也可以用new来获取一个实例:
functionclass1(){
//类成员的定义及构造函数
}
varobj1=newclass1();
抛开类的概念,从代码的形式上来看,class1就是一个函数,那么是不是所有的函数都可以用new来操作呢?
是的,在JavaScript中,函数和类就是一个概念,当对一个函数进行new操作时,就会返回一个对象。
如果这个函数中没有初始化类成员,那就会返回一个空的对象。
例如:
//定义一个hello函数
functionhello(){
alert("hello");
}
//通过new一个函数获得一个对象
varobj=newhello();
alert(typeof(obj));
从运行结果看,执行了hello函数,同时obj也获得了一个对象的引用。
当new一个函数时,这个函数就是所代表类的构造函数,其中的代码被看作为了初始化一个对象。
用于表示类的函数也称为构造器。
6.1.3 使用方括号([])引用对象的属性和方法
在JavaScript中,每个对象可以看作是多个属性(方法)的集合,引用一个属性(方法)很简单,如:
对象名.属性(方法)名
还可以用方括号的形式来引用:
对象名["属性(方法)名"]
注意,这里的方法名和属性名是一个字符串,不是原先点(?
)号后面的标识符,例如:
vararr=newArray();
//为数组添加一个元素
arr["push"]("abc");
//获得数组的长度
varlen=arr["length"];
//输出数组的长度
alert(len);
图6.1显示了执行的结果。
由此可见,上面的代码等价于:
vararr=newArray();
//为数组添加一个元素
arr.push("abc");
//获得数组的长度
varlen=arr.length;
//输出数组的长度
alert(len);
这种引用属性(方法)的方式和数组类似,体现了JavaScript对象就是一组属性(方法)的集合这个性质。
这种用法适合不确定具体要引用哪个属性(方法)的情况,例如:
一个对象用于表示用户资料,用一个字符串表示要使用的那个属性,就可以用这种方式来引用:
--
//定义了一个User类,包括两个成员age和sex,并指定了初始值。
functionUser(){
this.age=21;
this.sex="male";
}
//创建user对象
varuser=newUser();
//根据下拉列表框显示用户的信息
functionshow(slt){
if(slt.selectedIndex!
=0){
alert(user[slt.value]);
}
}
//-->
--下拉列表框用于选择用户信息-->
年龄
性别
在这段代码中,使用一个下拉列表框让用户选择查看哪个信息,每个选项的value就表示用户对象的属性名称。
这时如果不采用方括号的形式,可使用如下代码来实现:
functionshow(slt){
if(slt.selectedIndex!
=0){
if(slt.value=="age")alert(user.age);
if(slt.value=="sex")alert(user.sex);
}
}
而使用方括号语法,则只需写为:
alert(user[slt.value]);
方括号语法像一种参数语法,可用一个变量来表示引用对象的哪个属性。
如果不采用这种方法,又不想用条件判断,可以使用eval函数:
alert(eval("user."+slt.value));
这里利用eval函数的性质,执行了一段动态生成的代码,并返回了结果。
实际上,在前面讲述document的集合对象时,就有类似方括号的用法,比如引用页面中一个名为“theForm”的表单对象,以前的用法是:
document.forms["theForm"];
也可以改写为:
document.forms.theForm;
forms对象是一个内部对象,和自定义对象不同的是,它还可以用索引来引用其中的一个属性。
6.1.4 动态添加、修改、删除对象的属性和方法
上一节介绍了如何引用一个对象的属性和方法,现在介绍如何为一个对象添加、修改或者删除属性和方法。
其他语言中,对象一旦生成,就不可更改,要为一个对象添加、修改成员必须要在对应的类中修改,并重新实例化,程序也必须重新编译。
JavaScript提供了灵活的机制来修改对象的行为,可以动态添加、修改、删除属性和方法。
例如:
先用类Object来创建一个空对象user:
varuser=newObject();
1.添加属性
这时user对象没有任何属性和方法,可以为它动态的添加属性,例如:
user.name="jack";
user.age=21;
user.sex="male";
通过上述语句,user对象具有了三个属性:
name、age和sex。
下面输出这三个语句:
alert(user.name);
alert(user.age);
alert(user.sex);
由代码运行效果可知,三个属性已经完全属于user对象了。
2.添加方法
添加方法的过程和添加属性类似:
user.alert=function(){
alert("mynameis:
"+this.name);
}
这就为user对象添加了一个方法“alert”,通过执行它,弹出一个对话框显示自己的名字:
user.alert();
图6.2显示了执行的结果。
3.修改属性和方法
修改一个属性和方法的过程就是用新的属性替换旧的属性,例如:
user.name="tom";
user.alert=function(){
alert("hello,"+this.name);
}
这样就修改了user对象name属性的值和alert方法,它从显示“mynameis”对话框变为了显示“hello”对话框。
4.删除属性和方法
删除一个属性和方法的过程也很简单,就是将其置为undefined:
user.name=undefined;
user.alert=undefined;
这样就删除了name属性和alert方法。
在添加、修改或者删除属性时,和引用属性相同,也可以采用方括号([])语法:
user["name"]="tom";
使用这种方式还有一个特点,可以使用非标识符字符串作为属性名称,例如标识符中不允许以数字开头或者出现空格,但在方括号([])语法中却可以使用:
user["myname"]="tom";
需要注意,在使用这种非标识符作为名称的属性时,仍然要用方括号语法来引用:
alert(user["myname"]);
而不能写为:
alert(user.myname);
事实上,JavaScript中的每个对象都是动态可变的,这给编程带来了灵活性,也和其他语言产生了区别。
6.1.5 使用大括号({})语法创建无类型对象
传统的面向对象语言中,每个对象都会对应到一个类。
上一节讲this指针时提到,JavaScript中的对象其实就是属性(方法)的一个集合,并没有严格意义上类的概念。
所以它提供了一种简单的方式来创建对象,即大括号({})语法:
{
property1:
statement,
property2:
statement2,
…,
propertyN:
statmentN
}
通过大括号括住多个属性或方法及其定义(这些属性或方法用逗号隔开),来实现对象的定义,这段代码就直接定义个了具有n个属性或方法的对象,其中属性名和其定义之间用冒号(:
)隔开。
例如:
--
varobj={}; //定义了一个空对象
varuser={
name:
"jack", //定义了name属性,初始化为jack
favoriteColor:
["red","green","black","white"],//定义了颜色喜好数组
hello:
function(){ //定义了方法hello
alert("hello,"+this.name);
},
sex:
"male" //定义了性别属性sex,初始化为male
}
//调用user对象的方法hello
user.hello();
//-->
第一行定义了一个无类型对象obj,它等价于:
varobj=newObject();
接着定义了一个对象user及其属性和方法。
注意,除了最后一个属性(方法)定义,其他的必须以逗号(,)结尾。
其实,使用动态增减属性的方法也可以定义一个完全相同的user对象,读者可使用前面介绍的方法实现。
使用这种方式来定义对象,还可以使用字符串作为属性(方法)名,例如:
varobj={"001":
"abc"}
这就给对象obj定义了一个属性“001”,这并不是一个有效的标识符,所以要引用这个属性必须使用方括号语法:
obj["001"];
由此可见,无类型对象提供了一种创建对象的简便方式,它以紧凑和清晰的语法将一个对象体现为一个完整的实体。
而且也有利于减少代码的体积,这对JavaScript代码来说尤其重要,减少体积意味着提高了访问速度。
6.1.6 prototype原型对象
prototype对象是实现面向对象的一个重要机制。
每个函数(function)其实也是一个对象,它们对应的类是“Function”,但它们身份特殊,每个函数对象都具有一个子对象prototype。
即prototype表示了该函数的原型,而函数也是类,prototype就是表示了一个类的成员的集合。
当通过new来获取一个类的对象时,prototype对象的成员都会成为实例化对象的成员。
既然prototype是一个对象,可以使用前面两节介绍的方法对其进行动态的修改,这里先给出一个简单的例子:
//定义了一个空类
functionclass1(){
//empty
}
//对类的prototype对象进行修改,增加方法method
class1.prototype.method=function(){
alert("it'satestmethod");
}
//创建类class1的实例
varobj1=newclass1();
//调用obj1的方法method
obj1.method();
图6.3显示了执行的结果。
6.2 深入认识JavaScript中的函数
6.2.1 概述
函数是进行模块化程序设计的基础,编写复杂的Ajax应用程序,必须对函数有更深入的了解。
JavaScript中的函数不同于其他的语言,每个函数都是作为一个对象被维护和运行的。
通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或者将函数作为参数传递。
在继续讲述之前,先看一下函数的使用语法:
functionfunc1(…){…}
varfunc2=function(…){…};
varfunc3=functionfunc4(…){…};
varfunc5=newFunction();
这些都是声明函数的正确语法。
它们和其他语言中常见的函数或之前介绍的函数定义方式有着很大的区别。
那么在JavaScript中为什么能这么写?
它所遵循的语法是什么呢?
下面将介绍这些内容。
6.2.2 认识函数对象(FunctionObject)
可以用function关键字定义一个函数,并为每个函数指定一个函数名,通过函数名来进行调用。
在JavaScript解释执行时,函数都是被维护为一个对象,这就是要介绍的函数对象(FunctionObject)。
函数对象与其他用户所定义的对象有着本质的区别,这一类对象被称之为内部对象,例如日期对象(Date)、数组对象(Array)、字符串对象(String)都属于内部对象。
这些内置对象的构造器是由JavaScript本身所定义的:
通过执行newArray()这样的语句返回一个对象,JavaScript内部有一套机制来初始化返回的对象,而不是由用户来指定对象的构造方式。
在JavaScript中,函数对象对应的类型是Function,正如数组对象对应的类型是Array,日期对象对应的类型是Date一样,可以通过newFunction()来创建一个函数对象,也可以通过function关键字来创建一个对象。
为了便于理解,我们比较函数对象的创建和数组对象的创建。
先看数组对象:
下面两行代码都是创建一个数组对象myArray:
varmyArray=[];
//等价于
varmyArray=newArray();
同样,下面的两段代码也都是创建一个函数myFunction:
functionmyFunction(a,b){
returna+b;
}
//等价于
varmyFunction=newFunction("a","b","returna+b");
通过和构造数组对象语句的比较,可以清楚的看到函数对象本质,前面介绍的函数声明是上述代码的第一种方式,而在解释器内部,当遇到这种语法时,就会自动构造一个Function对象,将函数作为一个内部的对象来存储和运行。
从这里也可以看到,一个函数对象名称(函数变量)和一个普通变量名称具有同样的规范,都可以通过变量名来引用这个变量,但是函数变量名后面可以跟上括号和参数列表来进行函数调用。
用newFunction()的形式来创建一个函数不常见,因为一个函数体通常会有多条语句,如果将它们以一个字符串的形式作为参数传递,代码的可读性差。
下面介绍一下其使用语法:
varfuncName=newFunction(p1,p2,...,pn,body);
参数的类型都是字符串,p1到pn表示所创建函数的参数名称列表,body表示所创建函数的函数体语句,funcName就是所创建函数的名称。
可以不指定任何参数创建一个空函数,不指定funcName创建一个无名函数,当然那样的函数没有任何意义。
需要注意的是,p1到pn是参数名称的列表,即p1不仅能代表一个参数,它也可以是一个逗号隔开的参数列表,例如下面的定义是等价的:
newFunction("a","b","c","returna+b+c")
newFunction("a,b,c","returna+b+c")
newFunction("a,b","c","returna+b+c")
JavaScript引入Function类型并提供newFunction()这样的语法是因为函数对象添加属性和方法就必须借助于Function这个类型。
函数的本质是一个内部对象,由JavaScript解释器决定其运行方式。
通过上述代码创建的函数,在程序中可以使用函数名进行调用。
本节开头列出的函数定义问题也得到了解释。
注意可直接在函数声明后面加上括号就表示创建完成后立即进行函数调用,例如:
vari=function(a,b){
returna+b;
}(1,2);
alert(i);
这段代码会显示变量i的值等于3。
i是表示返回的值,而不是创建的函数,因为括号“(”比等号“=”有更高的优先级。
这样的代码可能并不常用,但当用户想在很长的代码段中进行模块化设计或者想避免命名冲突,这是一个不错的解决办法。
需要注意的是,尽管下面两种创建函数的方法是等价的:
functionfuncName(){
//函数体
}
//等价于
varfuncName=function(){
//函数体
}
但前面一种方式创建的是有名函数,而后面是创建了一个无名函数,只是让一个变量指向了这个无名函数。
在使用上仅有一点区别,就是:
对于有名函数,它可以出现在调用之后再定义;而对于无名函数,它必须是在调用之前就已经定义。
例如:
--
func();
varfunc=function(){
alert
(1)
}
//-->
这段语句将产生func未定义的错误,而:
--
func();
functionfunc(){
alert
(1)
}
//-->
则能够正确执行,下面的语句也能正确执行:
--
func();
varsomeFunc=functionfunc(){
alert
(1)
}
//-->
由此可见,尽管JavaScript是一门解释型的语言,但它会在函数调用时,检查整个代码中是否存在相应的函数定义,这个函数名只有是通过functionfuncName()形式定义的才会有效,而不能是匿名函数。
6.2.3 函数对象和其他内部对象的关系
除了函数对象,还有很多内部对象,比如:
Object、Array、Date、RegExp、Math、Error。
这些名称实际上表示一个类型,可以通过new操作符返回一个对象。
然而函数对象和其他对象不同,当用typeof得到一个函数对象的类型时,它仍然会返回字符串“function”,而typeof一个数组对象或其他的对象时,它会返回字符串“object”。
下面的代码示例了typeof不同类型的情况:
alert(typeof(Function)));
alert(typeof(newFunction()));
alert(typeof(Array));
alert(typeof(Object));
alert(typeof(newArray()));
alert(typeof(newDate()));
alert(typeof(newObject()));
运行这段代码可以发现:
前面4条语句都会显示“function”,而后面3条语句则显示“object”,可见new一个function实际上是返回一个函数。
这与其他的对象有很大的不同。
其他的类型Array、Object等都会通过new操作符返回一个普通对象。
尽管函数本身也是一个对象,但它与普通的对象还是有区别的,因为它同时也是对象构造器,也就是说,可以new一个函数来返回一个对象,这在前面已经介绍。
所有typeof返回“function”的对象都是函数对象。
也称这样的对象为构造器(constructor),因而,所有的构造器都是对象,但不是所有的对象都是构造器。
既然函数本身也是一个对象,它们的类型是function,联想到C++、Java等面向对象语言的类定义,可以猜测到Function类型的作用所在,那就是可以给函数对象本身定义一些方法和属性,借助于函数的prototype对象,可以很方便地修改和扩充Function类型的定义,例如下面扩展了函数类型Function,为其增加了method1方法,作用是弹出对话框显示"function":
Function.prototype.method1=function(){
alert("function");
}
functionfunc1(a,b,c){
returna+b+c;
}
func1.method1();
func1.method1.method1();
注意最后一个语句:
func1.method1.mehotd1(),它调用了method1这个函数对象的method1方法。
虽然看上去有点容易混淆,但仔细观察一下语法还是很明确的:
这是一个递归的定义。
因为method1本身也是一个函数,所以它同样具有函数对象的属性和方法,所有对Function类型的方法扩充都具有这样的递归性质。
Function是所有函数对象的基础,而Object则是所有对象(包括函数对象)的基础。
在JavaScript中,任何一个对象都是Object的实例,因此,可以修改Object这个类型来让所有的对象具有一些通用的属性和方法,修改Object类型是通过prototype来完成的:
Object.prototype.getType=function(){
returntypeof(this);
}
vararray1=newArray();
functionfunc1(a,b){
returna+b;
}
alert(array1.getType());
alert(func1.getType());
上面的代码为所有的对象添加了getType方法,作用是返回该对象的类型。
两条alert语句分别会显示“object”和“function”。
6.2.4 将函数作为参数传递
在前面已经介绍了函数对象本质,每个函数都被表示为一个特殊的对象,可以方便的将其赋值给一个变量,再通过这个变量名进行函数调用。