JS面向对象教程

上传人:仙*** 文档编号:130949532 上传时间:2022-08-05 格式:DOC 页数:34 大小:384KB
返回 下载 相关 举报
JS面向对象教程_第1页
第1页 / 共34页
JS面向对象教程_第2页
第2页 / 共34页
JS面向对象教程_第3页
第3页 / 共34页
点击查看更多>>
资源描述
6.1 JavaScript中支持面向对象的基础6.1.1 用定义函数的方式定义类在面向对象的思想中,最核心的概念之一就是类。一个类表示了具有相似性质的一类事物的抽象,通过实例化一个类,可以获得属于该类的一个实例,即对象。在JavaScript中定义一个类的方法如下:functionclass1()/类成员的定义及构造函数这里classl既是一个函数也是一个类。可以将它理解为类的构造函数,负责初始化工作。612使用new操作符获得一个类的实例在前面介绍基本对象时,已经用过new操作符,例如:newDate();表示创建一个日期对象,而Date就是表示日期的类,只是这个类是由JavaScript内部提供的,而不是由用户定义的。new操作符不仅对内部类有效,对用户定义的类也同样有效,对于上节定义的classl,也可以用new来获取一个实例:functionclass1()/类成员的定义及构造函数varobj1=newclass1();JavaScript抛开类的概念,从代码的形式上来看,classl000000,那么是不是所有的函数都可以用new来操作呢?是的,在中,函数和类就是一个概念,当对一个函数进行new操作时,就会返回一个对象。如果这个函数中没有初始化类成员,那就会返回一个空的对象。例如:定义一个hello00functionhello()alert(hello);通过new0000获得一个对象varobj=newhello();alert(typeof(obj);从运行结果看,执行了hello00,0Dobj也获得了00对象的引用。当new0000时,这0000是所代表类的构造函0,其中的代码被看作为了初始化00对象。用于表示类的00也称为构造器。6.1.3使用方括号()引用对象的属性和方法在JavaScript中,每0对象可以看作是多0属性(方法)的集合,引用00属性(方法)很简单,如:对象名.属性(方法)名还可以用方括号的形式来引用:对象名属性(方法)名注意,这里的方法名和属性名是00字符串,不是原先点(?)号后面的标识符,例如:vararr=newArray();/为数组添加一个元素arrpush(abc);/获得数组的长度varlen=arrlength;/输出数组的长度alert(len);图6.1显示了执行的结果。由此可见,上面的代码等价于:vararr=newArray();/为数组添加一个元素arr.push(abc);/获得数组的长度varlen=arr.length;/输出数组的长度alert(len);这种引用属性(方法)的方式和数组类似,体现了JavaScript对象就是一组属性(方法)的集合这个性质。这种用法适合不确定具体要引用哪个属性(方法)的情况,例如:一个对象用于表示用户资料,用一个字符串表示要使用的那个属性,就可以用这种方式来引用:请选择需要查看的信息:voptionvalue=age年龄性另U在这段代码中,使用一个下拉列表框让用户选择查看哪个信息,每个选项的value就表示用户对象的属性名称。这时如果不采用方括号的形式,可使用如下代码来实现:functionshow(slt)if(slt.selectedIndex!=0)if(slt.value=age)alert(user.age);if(slt.value=sex)alert(user.sex);而使用方括号语法,则只需写为:alert(userslt.value);方括号语法像一种参数语法,可用一个变量来表示引用对象的哪个属性。如果不采用这种方法,又不想用条件判断,可以使用eval函数:alert(eval(user.+slt.value);这里利用eval函数的性质,执行了一段动态生成的代码,并返回了结果。实际上,在前面讲述document的集合对象时,就有类似方括号的用法,比如引用页面中一个名为theForm的表单对象,以前的用法是:document.formstheForm;也可以改写为: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方法。在添加、修改或者删除属性时,和引用属性相同,也可以采用方括号()语法:username=tom;使用这种方式还有一个特点,可以使用非标识符字符串作为属性名称,例如标识符中不允许以数字开头或者出现空格,但在方括号()语法中却可以使用:usermyname=tom;需要注意,在使用这种非标识符作为名称的属性时,仍然要用方括号语法来引用:alert(usermyname);而不能写为:alert(user.myname);事实上,JavaScript中的每个对象都是动态可变的,这给编程带来了灵活性,也和其他语言产生了区别。6.1.5使用大括号()语法创建无类型对象传统的面向对象语言中,每个对象都会对应到一个类。上一节讲this指针时提到,JavaScript中的对象其实就是属性(方法)的一个集合,并没有严格意义上类的概念。所以它提供了一种简单的方式来创建对象,即大括号()语法:property1:statement,property2:statement2,propertyN:statmentN通过大括号括住多个属性或方法及其定义(这些属性或方法用逗号隔开),来实现对象的定义,这段代码就直接定义个了具有n个属性或方法的对象,其中属性名和其定义之间用冒号(:)隔开。例如:第一行定义了一个无类型对象obj,它等价于:varobj=newObject();接着定义了一个对象user及其属性和方法。注意,除了最后一个属性(方法)定义,其他的必须以逗号(,)结尾。其实,使用动态增减属性的方法也可以定义一个完全相同的user对象,读者可使用前面介绍的方法实现。使用这种方式来定义对象,还可以使用字符串作为属性(方法)名,例如:varobj=001:abc这就给对象obj定义了一个属性00T,这并不是一个有效的标识符,所以要引用这个属性必须使用方括号语法:obj001;由此可见,无类型对象提供了一种创建对象的简便方式,它以紧凑和清晰的语法将一个对象体现为一个完整的实体。而且也有利于减少代码的体积,这对JavaScript代码来说尤其重要,减少体积意味着提高了访问速度。6.1.6prototype原型对象prototype对象是实现面向对象的一个重要机制。每个函数(function)其实也是一个对象,它们对应的类是Function,但它们身份特殊,每个函数对象都具有一个子对象prototypeo即prototype表示了该函数的原型,而函数也是类,prototype就是表示了一个类的成员的集合。当通过new来获取一个类的对象时,prototype对象的成员都会成为实例化对象的成员。既然prototype是一个对象,可以使用前面两节介绍的方法对其进行动态的修改,这里先给出一个简单的例子:/定义了一个空类functionclass1()/empty/对类的prototype对象进行修改,增加方法methodclass1.prototype.method=function()alert(itsatestmethod);/创建类class1的实例varobj1=newclass1();调用obj1的方法methodobj1.method();峠InlcrrretCkpIofCtXJ寸testmethod图6.3显示了执行的结果。6.2深入认识JavaScript中的函数6.2.1概述函数是进行模块化程序设计的基础,编写复杂的Ajax应用程序,必须对函数有更深入的了解。JavaScript中的函数不同于其他的语言,每个函数都是作为一个对象被维护和运行的。通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或者将函数作为参数传递。在继续讲述之前,先看一下函数的使用语法:functionfunc1(.).varfunc2=function(.).;varfunc3=functionfunc4(.).;varfunc5=newFunction();这些都是声明函数的正确语法。它们和其他语言中常见的函数或之前介绍的函数定义方式有着很大的区别。那么在JavaScript中为什么能这么写?它所遵循的语法是什么呢?下面将介绍这些内容。622认识函数对象(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);参数的类型都是字符串,pl到pn表示所创建函数的参数名称列表,body表示所创建函数的函数体语句,funcName就是所创建函数的名称。可以不指定任何参数创建一个空函数,不指定funcName创建一个无名函数,当然那样的函数没有任何意义。需要注意的是,pl到pn是参数名称的列表,即pl不仅能代表一个参数,它也可以是一个逗号隔开的参数列表,例如下面的定义是等价的: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未定义的错误,而:则能够正确执行,下面的语句也能正确执行:由此可见,尽管JavaScript是一门解释型的语言,但它会在函数调用时,检查整个代码中是否存在相应的函数定义,这个函数名只有是通过functionfuncName()形式定义的才会有效,而不能是匿名函数。6.2.3函数对象和其他内部对象的关系除了函数对象,还有很多内部对象,比如:Object、Array、Date、RegExp、Math、Error。这些名称实际上表示一个类型,可以通过new操作符返回一个对象。然而函数对象和其他对象不同,当用typeof得到一个函数对象的类型时,它仍然会返回字符串function,而typeof一个数组对象或其他的对象时,它会返回字符串object。下面的代码示例了typeof不同类型的情况:alert(typeof(Function);/functionalert(typeof(newFunction();/functionalert(typeof(Array);/functionalert(typeof(Object);/functionalert(typeof(newArray();/objectalert(typeof(newDate();/objectalert(typeof(newObject();/object运行这段代码可以发现:前面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(),它调用了methodi这个函数对象的methodi方法。虽然看上去有点容易混淆,但仔细观察一下语法还是很明确的:这是一个递归的定义。因为methodi本身也是一个函数,所以它同样具有函数对象的属性和方法,所有对Function类型的方法扩充都具有这样的递归性质。Function是所有函数对象的基础,而Object则是所有对象(包括函数对象)的基础。在JavaScript中,任何一个对象都是Object的实例,因此,可以修改Object这个类型来让所有的对象具有一些通用的属性和方法,修改Object类型是通过prototype来完成的:Object.prototype.getType=function()returntypeof(this);vararrayi=newArray();functionfunci(a,b)returna+b;alert(arrayi.getType();alert(funci.getType();上面的代码为所有的对象添加了getType方法,作用是返回该对象的类型。两条alert语句分别会显示object和function。6.2.4将函数作为参数传递在前面已经介绍了函数对象本质,每个函数都被表示为一个特殊的对象,可以方便的将其赋值给一个变量,再通过这个变量名进行函数调用。作为一个变量,它可以以参数的形式传递给另一个函数,这在前面介绍JavaScript事件处理机制中已经看到过这样的用法,例如下面的程序将func1作为参数传递给func2:functionfunci(theFunc)theFunc();functionfunc2()alert(ok);funci(func2);在最后一条语句中,func2作为一个对象传递给了func1的形参theFunc,再由func1内部进行theFunc的调用。事实上,将函数作为参数传递,或者是将函数赋值给其他变量是所有事件机制的基础。例如,如果需要在页面载入时进行一些初始化工作,可以先定义一个init的初始化函数,再通过window.onload=init;语句将其绑定到页面载入完成的事件。这里的init就是一个函数对象,它可以加入window的onload事件列表。625传递给函数的隐含参数:arguments当进行函数调用时,除了指定的参数外,还创建一个隐含的对象-argumentsoarguments是一个类似数组但不是数组的对象,说它类似是因为它具有数组一样的访问性质,可以用argumentsindex这样的语法取值,拥有数组长度属性length。arguments对象存储的是实际传递给函数的参数,而不局限于函数声明所定义的参数列表,例如:functionfunc(a,b)alert(a);alert(b);for(vari=0;iarguments.length;i+)alert(argumentsi);func(1,2,3);代码运行时会依次显示:1,2,1,2,3。因此,在定义函数的时候,即使不指定参数列表,仍然可以通过arguments引用到所获得的参数,这给编程带来了很大的灵活性。arguments对象的另一个属性是callee,它表示对函数对象本身的引用,这有利于实现无名函数的递归或者保证函数的封装性,例如使用递归来计算1到n的自然数之和:varsum=function(n)if(1=n)return1;elsereturnn+sum(n-1);alert(sum(100);其中函数内部包含了对sum自身的调用,然而对于JavaScript来说,函数名仅仅是一个变量名,在函数内部调用sum即相当于调用一个全局变量,不能很好的体现出是调用自身,所以使用arguments.callee属性会是一个较好的办法:varsum=function(n)if(1=n)return1;elsereturnn+arguments.callee(n-1);alert(sum(100);callee属性并不是arguments不同于数组对象的惟一特征,下面的代码说明了arguments不是由Array类型创建:Array.prototype.p1=1;alert(newArray().p1);functionfunc()alert(arguments.p1);func();运行代码可以发现,第一个alert语句显示为1,即表示数组对象拥有属性p1,而func调用则显示为undefined”,即p1不是arguments的属性,由此可见,arguments并不是一个数组对象。626函数的apply、call方法和length属性JavaScript为函数对象定义了两个方法:apply和call,它们的作用都是将函数绑定到另外一个对象上去运行,两者仅在定义参数的方式有所区别:Function.prototype.apply(thisArg,argArray);Function.prototype.call(thisArg,arg1,arg2.)从函数原型可以看到,第一个参数都被取名为thisArg,即所有函数内部的this指针都会被赋值为thisArg,这就实现了将函数作为另外一个对象的方法运行的目的。两个方法除了thisArg参数,都是为Function对象传递的参数。下面的代码说明了apply和call方法的工作方式:定义一个函数func1,具有属性p和方法Afunctionfunc1()this.p=func1-;this.A=function(arg)alert(this.p+arg);/定义一个函数func2,具有属性p和方法Bfunctionfunc2()this.p=func2-;this.B=function(arg)alert(this.p+arg);varobj1=newfunc1();varobj2=newfunc2();obj1.A(byA);/显示func1-byAobj2.B(byB);显示func2-byBobj1.A.apply(obj2,byA);/显示func2-byA,其中byA是仅有一个元素的数组,下同obj2.B.apply(obj1,byB);/显示func1-byBobj1.A.call(obj2,byA);/显示func2-byAobj2.B.call(obj1,byB);/显示func1-byB可以看出,objl的方法A被绑定到obj2运行后,整个函数A的运行环境就转移到了obj2,即this指针指向了obj2。同样obj2的函数B也可以绑定到objl对象去运行。代码的最后4行显示了apply和call函数参数形式的区别。与arguments的length属性不同,函数对象还有一个属性length,它表示函数定义时所指定参数的个数,而非调用时实际传递的参数个数。例如下面的代码将显示2:functionsum(a,b)returna+b;alert(sum.length);6.2.7深入认识JavaScript中的this指针this指针是面向对象程序设计中的一项重要概念,它表示当前运行的对象。在实现对象的方法时,可以使用this指针来获得该对象自身的引用。和其他面向对象的语言不同,JavaScript中的this指针是一个动态的变量,一个方法内的this指针并不是始终指向定义该方法的对象的,在上一节讲函数的apply和call方法时已经有过这样的例子。为了方便理解,再来看下面的例子:从代码的执行结果看,分别弹出对话框显示1和2。由此可见,getP函数仅定义了一次,在不同的场合运行,显示了不同的运行结果,这是有this指针的变化所决定的。在objl的getP方法中,this就指向了objl对象,而在obj2的getP方法中,this就指向了obj2对象,并通过this指针引用到了两个对象都具有的属性p。由此可见,JavaScript中的this指针是一个动态变化的变量,它表明了当前运行该函数的对象。由this指针的性质,也可以更好的理解JavaScript中对象的本质:一个对象就是由一个或多个属性(方法)组成的集合。每个集合元素不是仅能属于一个集合,而是可以动态的属于多个集合。这样,一个方法(集合元素)由谁调用,this指针就指向谁。实际上,前面介绍的apply方法和call方法都是通过强制改变this指针的值来实现的,使this指针指向参数所指定的对象,从而达到将一个对象的方法作为另一个对象的方法运行。每个对象集合的元素(即属性或方法)也是一个独立的部分,全局函数和作为一个对象方法定义的函数之间没有任何区别,因为可以把全局函数和变量看作为window对象的方法和属性。也可以使用new操作符来操作一个对象的方法来返回一个对象,这样一个对象的方法也就可以定义为类的形式,其中的this指针则会指向新创建的对象。在后面可以看到,这时对象名可以起到一个命名空间的作用,这是使用JavaScript进行面向对象程序设计的一个技巧。例如:varnamespace1=newObject();namespace1.class1=function()/初始化对象的代码varobj1=newnamespace1.class1();这里就可以把namespace1看成一个命名空间。由于对象属性(方法)的动态变化特性,一个对象的两个属性(方法)之间的互相引用,必须要通过this指针,而其他语言中,this关键字是可以省略的。如上面的例子中:obj1.getP=function()alert(this.p);表面上this指针指向的是obj1这里的this关键字是不可省略的,即不能写成alert(p)的形式。这将使得getP函数去引用上下文环境中的p变量,而不是obj1的属性。6.36.3.1理解类的实现机制在JavaScript中可以使用function关键字来定义一个类,如何为类添加成员。在函数内通过this指针引用的变量或者方法都会成为类的成员,例如:functionclass1()vars=abc;this.p1=s;this.method1=function()alert(thisisatestmethod);varobj1=newclass1();通过newclass1()获得对象objl,对象objl便自动获得了属性pl和方法methodi。在JavaScript中function本身的定义就是类的构造函数,结合前面介绍过的对象的性质以及new操作符的用法,下面介绍使用new创建对象的过程。(1) 当解释器遇到new操作符时便创建一个空对象;(2) 开始运行class1这个函数,并将其中的this指针都指向这个新建的对象;(3) 因为当给对象不存在的属性赋值时,解释器就会为对象创建该属性,例如在class1中,当执行到this.p1=s这条语句时,就会添加一个属性p1,并把变量s的值赋给它,这样函数执行就是初始化这个对象的过程,即实现构造函数的作用;(4) 当函数执行完后,new操作符就返回初始化后的对象。通过这整个过程,JavaScript中就实现了面向对象的基本机制。由此可见,在JavaScript中function的定义实际上就是实现一个对象的构造器,是通过函数来完成的。这种方式的缺点是:?将所有的初始化语句、成员定义都放到一起,代码逻辑不够清晰,不易实现复杂的功能。?每创建一个类的实例,都要执行一次构造函数。构造函数中定义的属性和方法总被重复的创建,例如:this.method1=function()alert(thisisatestmethod);这里的method1每创建一个class1的实例,都会被创建一次,造成了内存的浪费。下一节介绍另一种类定义的机制:prototype对象,可以解决构造函数中定义类成员带来的缺点。6.3.2使用prototype对象定义类成员上一节介绍了类的实现机制以及构造函数的实现,现在介绍另一种为类添加成员的机制:prototype对象。当new一个function时,该对象的成员将自动赋给所创建的对象,例如:prototype是一个JavaScript对象,可以为prototype对象添加、修改、删除方法和属性。从而为一个类添加成员定义。了解了函数的prototype对象,现在再来看new的执行过程。(1) 创建一个新的对象,并让this指针指向它;(2) 将函数的prototype对象的所有成员都赋给这个新对象;3) 执行函数体,对这个对象进行初始化操作;4) 返回(1)中创建的对象。和上一节介绍的new的执行过程相比,多了用prototype来初始化对象的过程,这也和prototype的字面意思相符,它是所对应类的实例的原型。这个初始化过程发生在函数体(构造器)执行之前,所以可以在函数体内部调用prototype中定义的属性和方法,例如:和上一段代码相比,这里在classl的内部调用了prototype中定义的方法showProp,从而在对象的构造过程中就弹出了对话框,显示prop属性的值为1。需要注意,原型对象的定义必须在创建类实例的语句之前,否则它将不会起作用,例如:这段代码将会产生运行时错误,显示对象没有showProp方法,就是因为该方法的定义是在实例化一个类的语句之后。由此可见,prototype对象专用于设计类的成员,它是和一个类紧密相关的,除此之外,prototype还有一个重要的属性:constructor,表示对该构造函数的引用,例如:functionclass1()alert(1);class1.prototype.constructor();/调用类的构造函数这段代码运行后将会出现对话框,在上面显示文字T,从而可以看出一个prototype是和一个类的定义紧密相关的。实际上:class1.prototype.constructor=class1。6.3.3一种JavaScript类的设计模式前面已经介绍了如何定义一个类,如何初始化一个类的实例,且类可以在function定义的函数体中添加成员,又可以用prototype定义类的成员,编程的代码显得混乱。如何以一种清晰的方式来定义类呢?下面给出了一种类的实现模式。在JavaScript中,由于对象灵活的性质,在构造函数中也可以为类添加成员,在增加灵活性的同时,也增加了代码的复杂度。为了提高代码的可读性和开发效率,可以采用这种定义成员的方式,而使用prototype对象来替代,这样function的定义就是类的构造函数,符合传统意义类的实现:类名和构造函数名是相同的。例如:functionclass1()/构造函数/成员定义class1.prototype.someProperty=sample;class1.prototype.someMethod=function()/方法实现代码虽然上面的代码对于类的定义已经清晰了很多,但每定义一个属性或方法,都需要使用一次class1.prototype,不仅代码体积变大,而且易读性还不够。为了进一步改进,可以使用无类型对象的构造方法来指定prototype对象,从而实现类的成员定义:定义一个类classlfunctionclass1()/构造函数/通过指定prototype对象来实现类的成员定义class1.prototype=someProperty:sample,someMethod:function()/方法代码,/其他属性和方法.上面的代码用一种很清晰的方式定义了classl,构造函数直接用类名来实现,而成员使用无类型对象来定义,以列表的方式实现了所有属性和方法,并且可以在定义的同时初始化属性的值。这也更象传统意义面向对象语言中类的实现。只是构造函数和类的成员定义被分为了两个部分,这可看成JavaScript中定义类的一种固定模式,这样在使用时会更加容易理解。注意:在一个类的成员之间互相引用,必须通过this指针来进行,例如在上面例子中的someMethod方法中,如果要使用属性someProperty,必须通过this.someProperty的形式,因为在JavaScript中每个属性和方法都是独立的,它们通过this指针联系在一个对象上。6.4公有成员、私有成员和静态成员6.4.1实现类的公有成员前面定义的任何类成员都属于公有成员的范畴,该类的任何实例都对外公开这些属性和方法。6.4.2实现类的私有成员私有成员即在类的内部实现中可以共享的成员,不对外公开。JavaScript中并没有特殊的机制来定义私有成员,但可以用一些技巧来实现这个功能。这个技巧主要是通过变量的作用域性质来实现的,在JavaScript中,一个函数内部定义的变量称为局部变量,该变量不能够被此函数外的程序所访问,却可以被函数内部定义的嵌套函数所访问。在实现私有成员的过程中,正是利用了这一性质。前面提到,在类的构造函数中可以为类添加成员,通过这种方式定义的类成员,实际上共享了在构造函数内部定义的局部变量,这些变量就可以看作类的私有成员,例如:图6.4显示了运行的结果。这样,就实现了私有属性pp和私有方法pm。运行完classl以后,尽管看上去pp和pm这些局部变量应该随即消失,但实际上因为classl是通过new来运行的,它所属的对象还没消失,所以仍然可以通过公开成员来对它们进行操作。注意:这些局部变量(私有成员),被所有在构造函数中定义的公有方法所共享,而且仅被在构造函数中定义的公有方法所共享。这意味着,在prototype中定义的类成员将不能访问在构造体中定义的局部变量(私有成员)。要使用私有成员,是以牺牲代码可读性为代价的。而且这种实现更多的是一种JavaScript技巧,因为它并不是语言本身具有的机制。但这种利用变量作用域性质的技巧,却是值得借鉴的。6.4.3实现静态成员静态成员属于一个类的成员,它可以通过类名静态成员名”的方式访问。在JavaScript中,可以给一个函数对象直接添加成员来实现静态成员,因为函数也是一个对象,所以对象的相关操作,对函数同样适用。例如:functionclassl()/构造函数/静态属性classl.staticProperty=sample;/静态方法class1.staticMethod=function()alert(class1.staticProperty);/调用静态方法class1.staticMethod();通过上面的代码,就为类class1添加了一个静态属性和静态方法,并且在静态方法中引用了该类的静态属性。如果要给每个函数对象都添加通用的静态方法,还可以通过函数对象所对应的类Function来实现,例如:/给类Function添加原型方法:showArgsCountFunction.prototype.showArgsCount=function()alert(this.length);/显示函数定义的形参的个数functionclass1(a)/定义一个类调用通过Function的prototype定义的类的静态方法showArgsCountclass1.showArgsCount();由此可见,通过Function的prototype原型对象,可以给任何函数都加上通用的静态成员,这在实际开发中可以起到很大的作用,比如在著名的prototype-1.3.1.js框架中,就给所有的函数定义了以下两个方法:/将函数作为一个对象的方法运行Function.prototype.bind=function(object)var_method=this;returnfunction()_method.apply(object,arguments);/将函数作为事件监听器Function.prototype.bindAsEventListener=function(object)var_method=this;returnfunction(event)_method.call(object,event|window.event);这两个方法在prototype-1.3.1框架中起了很大的作用,具体含义及用法将在后面章节介绍。6.5使用for(.in实现反射机制6.5.1什么是反射机制反射机制指的是程序在运行时能够获取自身的信息。例如一个对象能够在运行时知道自己有哪些方法和属性。652在JavaScript中利用for(in)语句实现反射在JavaScript中有一个很方便的语法来实现反射,即for(in.)语句,其语法如下:for(varpinobj)/语句这里varp表示声明的一个变量,用以存储对象obj的属性(方法)名称,有了对象名和属性(方法)名,就可以使用方括号语法来调用一个对象的属性(方法):for(varpinobj)if(typeof(objp=function)objp();elsealert(objp);这段语句遍历obj对
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!