首页 > > > 征服Ajax - Web20开发技术详解试读版.pdf

征服Ajax - Web20开发技术详解试读版.pdf

征服Ajax - Web20开发技术详解试读版.pdf

上传者: al_10000 2014-02-20 评分1 评论0 下载1 收藏10 阅读量140 暂无简介 简介 举报

简介:本文档为《征服Ajax - Web20开发技术详解试读版pdf》,可适用于软件工程领域,主题内容包含《征服Ajax:Web开发技术详解》试读版作者:王沛冯曼菲出版社:人民邮电出版社全国各大书店均有销售网上购书地址:http:wwwdearbookc符等。

《征服 Ajax:Web2.0开发技术详解》试读版 作者:王沛 冯曼菲 出版社:人民邮电出版社 全国各大书店均有销售 网上购书地址:http://www.dearbook.com.cn/book/106884 http://www.china-pub.com/computers/common/info.asp?id=29998 PDF 文件使用 "pdfFactory" 试用版本创建 www.fineprint.cn 1 第 6章 JavaScript面向对象程序设计 在传统的Web开发模式中,JavaScript是一种点缀的作用,完成很有限的功能,诸如表 单验证之类。其语言本身也一直被当作过程化的语言使用,很难完成复杂的功能。而 Ajax 的出现使得复杂脚本成为必需的组成部分,这就对 JavaScript程序设计提出了新的要求,很 多 Ajax 应用开始利用 JavaScript 面向对象的性质进行开发,使逻辑更加清晰。事实上, JavaScript提供了完善的机制来实现面向对象的开发思想。 本章假设读者已经了解面向对象思想的基本概念,熟悉对象、类、继承等基本术语。以 此为基础,将重点介绍如何在 JavaScript中使用面向对象的思想,包括实现的原理、机制和 技巧。 6.1 JavaScript中支持面向对象的基础 6.1.1 用定义函数的方式定义类 在面向对象的思想中,最核心的概念之一就是类。一个类表示了具有相似性质的一类事 物的抽象,通过实例化一个类,可以获得属于该类的一个实例(即对象)。 在 JavaScript中定义一个类的方法如下: function class1(){ //类成员的定义及构造函数 } 这里 class1既是一个函数也是一个类。作为函数,它可以理解为类的构造函数,负责初 始化的工作。 6.1.2 使用 new操作符获得一个类的实例 在前面介绍基本对象时,已经用过 new操作符,例如: new Date(); 表示创建一个日期对象,而 Date 就是表示日期的类,只是这个类是由 JavaScript 内部 提供的,而不是由用户定义的。 new 操作符不仅对内部类有效,对用户定义的类也是同样的用法,对于上节定义的 class1,也可以用 new来获取一个实例: function class1(){ //类成员的定义及构造函数 } var obj1=new class1(); 抛开类的概念,从代码的形式上来看,class1就是一个函数,那么是不是所有的函数都 可以用 new来操作呢?答案是肯定的。在 JavaScript中,函数和类就是一个概念,当 new一 个函数时,就会返回一个对象。如果这个函数中没有初始化类成员,那就会返回一个空的对 象。例如: //定义一个 hello函数 function hello(){ PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 2 alert(“hello”); } //通过 new一个函数获得一个对象 var obj=new hello(); alert(typeof(obj)); 从运行结果看,执行了 hello函数,同时 obj也获得了一个对象的引用。事实上,当 new 一个函数时,这个函数就是所代表类的构造函数,其中的所有代码都可以看作为了初始化一 个对象而工作。用于表示类的函数也称之为构造器。 6.1.3 使用方括号([])引用对象的属性和方法 在 JavaScript中,每个对象可以看作是多个属性(方法)的集合,引用一个属性(方法) 很简单,即: 对象名.属性(方法)名 除此之外,还可以用方括号的形式来引用: 对象名[“属性(方法)名”] 注意,这里的方法名和属性名是一个字符串,而非原先点号后面的标识符,例如: var arr=new Array(); //为数组添加一个元素 arr[“push”](“abc”); //获得数组的长度 var len=arr[“length”]; //输出数组的长度 alert(len); 图 4.1显示了执行的结果。 图 4.1 引用对象属性示例 由此可见,上面的代码等价于: var arr=new Array(); //为数组添加一个元素 arr.push( “abc”); //获得数组的长度 var len=arr.length; //输出数组的长度 alert(len); 这种引用属性(方法)的方式和数组类似,也体现出一个 JavaScript对象就是一组属性 (方法)的集合这个性质。 这种用法适合不确定具体要引用哪个属性(方法)的场合,例如:一个对象用于表示用 户资料,这时一个字符串表示要使用哪个属性,那就可以用这种方式来引用: <script language="JavaScript" type="text/javascript"> <!-- //定义了一个 User类,包括两个成员 age和 sex,并指定了初始值。 PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 3 function User(){ this.age=21; this.sex="male"; } //创建 user对象 var user=new User(); //根据下拉列表框显示用户的信息 function show(slt){ if(slt.selectedIndex!=0){ alert(user[slt.value]); } } //--> </script> <!--下拉列表框用于选择用户信息--> <select onchange="show(this)"> <option>请选择需要查看的信息:</option> <option value="age">年龄</option> <option value="sex">性别</option> </select> 在这段代码中,使用一个下拉列表框让用户选择查看哪个信息,每个选项的 value就表 示用户对象的属性名称。这时如果不采用方括号的形式,就必须使用如下代码来达到预期效 果: function show(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 中却非如此,它提 PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 4 供了灵活的机制来修改对象的行为,可以动态添加、修改、删除属性和方法。例如首先使用 类 Object来创建一个空对象 user: var user=new Object(); 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(“my name is:”+this.name); } 这就为 user 对象添加了一个方法“alert”,通过执行它,可以弹出一个对话框显示自己 的名字介绍: user.alert(); 图 4.2显示了执行的结果。 图 4.2 为 user对象添加 alert方法示例 3.修改属性 修改一个属性的过程就是用新的属性替换旧的属性,例如: user.name=”tom”; user.alert=function(){ alert(“hello,”+this.name); } 这样就修改了 user对象 name属性的值和 alert方法,它从显示“my name is”变为了显 示“hello”。 4.删除属性 删除一个属性的过程也很简单,就是将其置为 undefined: user.name=undefined; user.alert=undefined; 这样就删除了 name属性和 alert方法。在之后的代码中,这些属性变的不可用。 在添加、修改或者删除属性时,和引用属性相同,也可以采用方括号([])语法: user[“name”]=”tom”; PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 5 使用这种方式还有一个额外的特点,就是可以使用非标识符字符串作为属性名称,例如 标识符中不允许以数字开头或者出现空格,但在方括号([])语法中却可以使用: user[“my name”]=”tom”; 需要注意,在使用这种非标识符作为名称的属性时,仍然要用方括号语法来引用: alert(user[“my name”]); 而不能写为: alert(user.my name); 利用对象的这种性质,甚至可以很容易实现一个简单的哈希表,在本书的后面将会看到 其应用。此可见,JavaScript中的每个对象都是动态可变的,这给编程带来了很大的灵活性, 也和其他语言产生了很大的区别,读者可以体会这种性质。 6.1.5 使用大括号({})语法创建无类型对象 传统的面向对象语言中,每个对象都会对应到一个类。而上一节讲 this 指针时提到, JavaScript 中的对象其实就是属性(方法)的一个集合,并没有严格意义的类的概念。所以 它提供了另外一种简单的方式来创建对象,即大括号({})语法: { property1:statement, property2:statement2, …, propertyN:statmentN } 通过大括号括住多个属性或方法及其定义(这些属性或方法用逗号隔开),来实现对象 的定义,这段代码就直接定义个了具有 n个属性或方法的对象,其中属性名和其定义之间用 冒号(:)隔开。例如: <script language="JavaScript" type="text/javascript"> <!-- var obj={}; //定义了一个空对象 var user={ name:"jack", //定义了 name属性,初始化为 jack favoriteColor:["red","green","black","white"],//定义了颜色喜好数组 hello:function(){ //定义了方法 hello alert("hello,"+this.name); }, sex:"male" //定义了性别属性 sex,初始化为 sex } //调用 user对象的方法 hello user.hello(); //--> </script> 第一行定义了一个无类型对象 obj,它等价于: var obj=new Object(); 接着定义了一个对象 user 及其属性和方法。注意,除了最后一个属性(方法)定义, 其他的必须以逗号(,)结尾。其实,使用动态增减属性的方法也可以定义一个完全相同的 user对象,读者不妨使用前面介绍的方法做一个尝试。 使用这种方式来定义对象,还可以使用字符串作为属性(方法)名,例如: var obj={“001”:”abc”} PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 6 这就给对象 obj定义了一个属性“001”,这并不是一个有效的标识符,所以要引用这个 属性必须使用方括号语法: obj[“001”]; 由此可见,无类型对象提供了一种创建对象的简便方式,它以紧凑和清晰的语法将一个 对象体现为一个完整的实体。而且也有利于减少代码的体积,这对 JavaScript代码来说尤其 重要,因为要通过网络来下载,减少体积意味着提高了访问速度。 6.1.6 prototype原型对象 prototype 对象是实现面向对象的一个重要机制。每个函数(function)其实也是一个对 象,它们对应的类是“Function”,但它们身份特殊,每个函数对象都具有一个子对象 prototype。 顾名思义,prototype 表示了该函数的原型,而函数也即是类,prototype 实际上就是表示了 一个类的成员的集合。当通过 new 来获取一个类的对象时,prototype 对象的成员都会成为 实例化对象的成员。 既然 prototype 是一个对象,可以使用前面两节介绍的方法对其进行动态的修改,这里 先给出一个简单的例子: //定义了一个空类 function class1(){ //empty } //对类的 prototype对象进行修改,增加方法method class1.prototype.method=function(){ alert(“it’s a test method”); } //创建类 class1的实例 var obj1=new class1(); //调用 obj1的方法method obj1.method(); 图 4.3显示了执行的结果。 图 4.3 使用 prototype示例 6.2 深入认识 JavaScript中的函数 6.2.1 概述 函数是进行模块化程序设计的基础,然而到现在为止对 JavaScript中函数的介绍还停留 在一个很基础的阶段。尽管这已经足以应付一般的应用开发,但是对于复杂的 Ajax应用, 必须对函数有更深入的了解。JavaScript 中的函数不同于其它的语言,每个函数都是作为一 PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 7 个对象被维护和运行的。通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或 者将函数作为参数传递。在继续讲述之前,先看一下函数的使用语法: function func1(…){…} var func2=function(…){…}; var func3=function func4(…){…}; var func5=new Function(); 或许现在这些代码看上去有点匪夷所思,但这些都是声明函数的正确语法。它们和其它 语言中常见的函数,或者之前介绍的函数定义方式有着很大的区别。那么在 JavaScript中为 什么能这么写?它所遵循的语法是什么呢?这些正是下面要介绍的内容。 6.2.2 认识函数对象(Function Object) 可以用 function关键字定义一个函数,对于每个函数可以为其指定一个函数名,通过函 数名来进行调用。这些都是代码给用户的印象,而在 JavaScript解释执行的时候,实际上每 个函数都是被维护为一个对象,这就是本小节将要介绍的函数对象(Function Object)。 函数对象与其它用户所定义的对象有着本质的区别,这一类对象被称之为内部对象,例 如日期对象(Date)、数组对象(Array)、字符串对象(String)都是属于内部对象。换句话 说,这些内置对象的构造器是由 JavaScript本身所定义的:通过执行 new Array()这样的语句 返回一个对象,JavaScript 内部有一套机制来初始化返回的对象,而不是由用户来指定对象 的构造方式。 在 JavaScript中,函数对象对应的类型是 Function,正如数组对象对应的类型是 Array, 日期对象对应的类型是 Date一样,可以通过 new Function()来创建一个函数对象,也可以通 过 function关键字来创建一个对象。为了便于理解,将函数对象的创建和数组对象的创建来 比较。先看数组对象:下面两行代码的作用是一样的,都是创建一个数组对象 myArray: var myArray=[]; //等价于 var myArray=new Array(); 同样,下面的两段代码也是等价的,都是创建一个函数 myFunction: function myFunction(a,b){ return a+b; } //等价于 var myFunction=new Function("a","b","return a+b"); 现在上面的代码还有些难以理解,但是通过和构造数组对象语句的比较,可以清楚的看 到函数的对象本质,前面介绍的函数声明是上述代码的第一种方式,而在解释器内部,当遇 到这种语法时,就会自动构造一个 Function 对象,将函数作为一个内部的对象来存储和运 行。从这里也可以看到,一个函数对象名称(函数变量)和一个普通变量名称具有同样的规 范,都可以通过变量名来引用这个变量,但是函数变量名后面可以跟上括号和参数列表来进 行函数调用。 也许不会有人通过 new Function()的形式来创建一个函数,因为一个函数体通常会有多 条语句,如果将它们以一个字符串的形式作为参数传递,那么代码的可读性会非常的差。下 面介绍一下其使用语法: var funcName=new Function(p1,p2,...,pn,body); 参数的类型都是字符串,p1到 pn表示所创建函数的参数名称列表,body表示所创建函 数的函数体语句,而 funcName就是所创建函数的名称了。可以不指定任何参数创建一个空 函数,不指定 funcName创建一个无名函数,当然那样的函数什么用处都没有。 PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 8 需要注意的是,前面说 p1到 pn是参数名称的列表,这意味着 p1不仅仅只能代表一个 参数,它也可以是一个逗号格开的参数列表,例如下面的定义是等价的: new Function("a", "b", "c", "return a+b+c") new Function("a, b, c", "return a+b+c") new Function("a,b", "c", "return a+b+c") JavaScript引入 Function类型并提供 new Function()这样的语法来创建函数并不是毫无意 义的,在后面可以看到,函数作为一个对象,它本身就可以具有一些方法和属性,而为函数 对象添加属性和方法就必须借助于 Function这个类型。 现在已经认识到了函数的本质,它其实是一个内部对象,由 JavaScript解释器决定其运 行方式。通过上述代码创建的函数,在程序中可以使用函数名进行调用。于是在本节开头列 出的函数定义问题也得到了解释:它们都是创建函数对象的正确语法。注意直接在函数声明 后面加上括号就表示创建完成后立即进行函数调用,例如: var i=function (a,b){ return a+b; }(1,2); alert(i); 这段代码会显示变量 i 的值等于 3。i 是表示返回的值,而不是创建的函数,因为括号 “(”比等号“=”有更高的优先级。这样的代码可能并不常用,但当用户想在很长的代码段 中进行模块化设计或者想避免命名冲突,这是一个不错的解决办法。 需要注意的是,尽管下面两种创建函数的方法是等价的: function funcName(){ //函数体 } //等价于 var funcName=function(){ //函数体 } 但前面一种方式创建的是有名函数,而后面是创建了一个无名函数,只是让一个变量指 向了这个无名函数。在使用上仅有一点区别,就是:对于有名函数,它可以出现在调用之后 再定义;而对于无名函数,它必须是在调用之前就已经定义。例如: <script language="JavaScript" type="text/javascript"> <!-- func(); var func=function(){ alert(1) } //--> </script> 这段语句将产生 func未定义的错误,而: <script language="JavaScript" type="text/javascript"> <!-- func(); function func(){ alert(1) } //--> </script> 则能够正确执行,甚至下面的语句也能正确执行: <script language="JavaScript" type="text/javascript"> <!-- PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 9 func(); var someFunc=function func(){ alert(1) } //--> </script> 由此可见,尽管 JavaScript是一门解释型的语言,但它会在进行函数调用时,检查整个 代码中是否存在相应的函数定义,这个函数名只有是通过 function funcName()形式定义的才 会有效,而不能是匿名函数。 6.2.3 函数对象和其他内部对象的关系 除了函数对象,还有很多内部对象,比如:Object、Array、Date、RegExp、Math、Error。 更准确的说,这些名称实际上表示一个类型,可以通过 new 操作符返回一个对象。然而函 数对象和其它对象不同,当用 typeof 得到一个函数对象的类型时,它仍然会返回字符串 “function”,而 typeof一个数组对象或其它的对象时,它会返回字符串“object”。下面的代 码示例了 typeof不同类型的情况: alert(typeof(Function))); alert(typeof(new Function())); alert(typeof(Array)); alert(typeof(Object)); alert(typeof(new Array())); alert(typeof(new Date())); alert(typeof(new 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"); } function func1(a,b,c){ return a+b+c; } func1.method1(); func1.method1.method1(); 注意最后一个语句:func1.method1.mehotd1(),它调用了 method1 这个函数对象的 method1方法。虽然看上去有点容易混淆,但仔细观察一下语法还是很明确的:这是一个递 归的定义。因为 method1本身也是一个函数,所以它同样具有函数对象的属性和方法,所有 PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 10 对 Function类型的方法扩充都具有这样的递归性质。 Function是所有函数对象的基础,而 Object则是所有对象(包括函数对象)的基础。在 JavaScript中,任何一个对象都是 Object的实例,因此,可以修改 Object这个类型来让所有 的对象具有一些通用的属性和方法,修改 Object类型是通过 prototype来完成的: Object.prototype.getType=function(){ return typeof(this); } var array1=new Array(); function func1(a,b){ return a+b; } alert(array1.getType()); alert(func1.getType()); 上面的代码为所有的对象添加了 getType 方法,作用是返回该对象的类型。两条 alert 语句分别会显示“object”和“function”。 6.2.4 将函数作为参数传递 在前面已经介绍了函数的对象本质,每个函数在都被表示为一个特殊的对象,可以方便 的将其赋值给一个变量,再通过这个变量名进行函数调用。作为一个变量,它可以以参数的 形式传递给另一个函数,这在前面介绍 JavaScript事件处理机制中已经看到过这样的用法, 或许前面还对语法难以理解,但到了现在一切都变得顺其自然,例如下面的程序将 func1作 为参数传递给 func2: function func1(theFunc){ theFunc(); } function func2(){ alert("ok"); } func1(func2); 在最后一条语句中,func2作为一个对象传递给了 func1的形参 theFunc,再由 func1内 部进行 theFunc的调用。事实上,将函数作为参数传递,或者是将函数赋值给其它变量是所 有事件机制的基础。 例如,如果需要在页面载入时进行一些初始化工作,可以先定义一个 init的初始化函数, 再通过 window.onload=init;语句将其绑定到页面载入完成的事件。这里的 init就是一个函数 对象,它可以加入 window的 onload事件列表,也正源于它的对象本质,这和 C语言、C++ 语言的函数指针概念有很大的相似性。 6.2.5 传递给函数的隐含参数:arguments 当进行函数调用时,除了指定的参数外,还创建一个隐含的对象——arguments。 arguments是一个类似数组但不是数组的对象,说它类似是因为它具有数组一样的访问性质, 可以用 arguments[index]这样的语法取值,拥有数组长度属性 length。arguments对象存储的 是实际传递给函数的参数,而不只局限于函数声明所定义的参数列表,例如: function func(a,b){ alert(a); PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 11 alert(b); for(var i=0;i<arguments.length;i++){ alert(arguments[i]); } } func(1,2,3); 代码运行时会依次显示:1,2,1,2,3。因此,在定义函数的时候,即使不指定参数 列表,仍然可以通过 arguments 引用到所获得的参数,这给编程带来了很大的灵活性。 arguments对象的另一个属性是 callee,它表示对函数对象本身的引用,这有利于实现无名函 数的递归或者保证函数的封装性,例如使用递归来计算 1到 n的自然数之和: var sum=function(n){ if(1==n)return 1; else return n+sum(n-1); } alert(sum(100)); 其中函数内部包含了对 sum自身的调用,然而对于 JavaScript来说,函数名仅仅是一个 变量名,在函数内部调用 sum即相当于调用一个全局变量,不能很好的体现出是调用自身, 所以使用 arguments.callee属性会是一个较好的办法: var sum=function(n){ if(1==n)return 1; else return n+arguments.callee(n-1); } alert(sum(100)); callee属性并不是 arguments不同于数组对象的唯一特征,下面的代码说明了 arguments 不是由 Array类型创建: Array.prototype.p1=1; alert(new Array().p1); function func(){ alert(arguments.p1); } func(); 运行代码可以发现,第一个 alert语句显示为 1,即表示数组对象拥有属性 p1,而 func 调用则显示为“undefined”,即 p1不是 arguments的属性,由此可见,arguments并不是一个 数组对象。 6.2.6 函数的 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和方法 A function func1(){ this.p="func1-"; PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 12 this.A=function(arg){ alert(this.p+arg); } } //定义一个函数 func2,具有属性 p和方法 B function func2(){ this.p="func2-"; this.B=function(arg){ alert(this.p+arg); } } var obj1=new func1(); var obj2=new func2(); obj1.A("byA"); //显示 func1-byA obj2.B("byB"); //显示 func2-byB obj1.A.apply(obj2,["byA"]); //显示 func2-byA,其中[“byA”]是仅有一个元素的数组,下同 obj2.B.apply(obj1,["byB"]); //显示 func1-byB obj1.A.call(obj2,"byA"); //显示 func2-byA obj2.B.call(obj1,"byB"); //显示 func1-byB 可以看出,obj1的方法A被绑定到 obj2运行后,整个函数A的运行环境就转移到了 obj2, 即 this指针指向了 obj2。同样 obj2的函数 B也可以绑定到 obj1对象去运行。代码的最后 4 行显示了 apply和 call函数参数形式的区别。 与 arguments的 length属性不同,函数对象的还有一个参数相关的属性 length,它表示 函数定义时所指定参数的个数,而非调用时实际传递的参数个数。例如下面的代码将显示 2: function sum(a,b){ return a+b; } alert(sum.length); 6.2.7 深入认识 JavaScript中的 this指针 this指针是面向对象程序设计中的一项重要概念,它表示当前运行的对象。在实现对象 的方法时,可以使用 this指针来获得该对象自身的引用。 和传统意义的面向对象的语言不同,JavaScript 中的 this 指针是一个动态的变量,一个 方法内的 this指针并不是始终指向定义该方法的对象的,在上一节讲函数的 apply和 call方 法时已经有过这样的例子。为了方便大家理解,再来看下面的例子: <script language="JavaScript" type="text/javascript"> <!-- //创建两个空对象 var obj1=new Object(); var obj2=new Object(); //给两个对象都添加属性 p,并分别等于 1和 2 obj1.p=1; obj2.p=2; //给 obj1添加方法,用于显示 p的值 obj1.getP=function(){ alert(this.p); //表面上 this指针指向的是 obj1 } //调用 obj1的 getP方法 obj1.getP(); PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 13 //使 obj2的 getP方法等于 obj1的 getP方法 obj2.getP=obj1.getP; //调用 obj2的 getP方法 obj2.getP(); //--> </script> 从代码的执行结果看,分别弹出对话框 1和 2。由此可见,getP函数仅定义了一次,在 不同的场合运行,显示了不同的运行结果,这是有 this指针的变化所决定的。在 obj1的 getP 方法中,this就指向了 obj1对象,而在 obj2的 getP方法中,this就指向了 obj2对象,并通 过 this指针引用到了两个对象都具有的属性 p。 由此可见,JavaScript 中的 this 指针是一个动态变化的变量,它表明了当前运行该函数 的对象。由 this指针的性质,也可以更好的理解 JavaScript中对象的本质:一个对象就是由 一个或多个属性(方法)组成的集合。每个集合元素不是仅能属于一个集合,而是可以动态 的属于多个集合。这样,一个方法(集合元素)由谁调用,this指针就指向谁。实际上,前 面介绍的 apply方法和 call方法都是通过强制改变 this指针的值来实现的,使 this指针指向 参数所指定的对象,从而达到将一个对象的方法作为另一个对象的方法运行的效果。 同时,每个对象集合的元素(即属性或方法)也是一个独立的部分,全局函数和作为一 个对象方法定义的函数之间没有任何区别,因为可以把全局函数和变量看作为 window对象 的方法和属性。也可以使用 new 操作符来操作一个对象的方法来返回一个对象,这样一个 对象的方法也就可以定义为类的形式,其中的 this指针则会指向新创建的对象。在后面可以 看到,这时对象名可以起到一个命名空间的作用,这是使用 JavaScript进行面向对象程序设 计的一个技巧。例如: var namespace1=new Object(); namespace1.class1=function(){ //初始化对象的代码 } var obj1=new namespace1.class1(); 这里就可以把 namespace1看成一个命名空间。 由于对象属性(方法)的动态变化特性,一个对象的两个属性(方法)之间的互相引用, 必须要通过 this指针,而在传统语言中,this关键字是可以省略的。但是上面的例子中: obj1.getP=function(){ alert(this.p); //表面上 this指针指向的是 obj1 } 这里的 this关键字是不可省略的,即不能写成 alert(p)的形式。这将使得 getP函数去引 用上下文环境中的 p变量,而不是 obj1的属性。 6.3 类的实现 6.3.1 理解类的实现机制 在前面已经讲过,在 JavaScript中可以使用 function关键字来定义一个“类”。现在介绍 如何为类添加成员。其过程很简单,在函数内通过 this指针引用的变量或者方法都会成为类 的成员,例如: function class1(){ var s=”abc”; PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 14 this.p1=s; this.method1=function(){ alert(“this is a test method”); } } var obj1=new class1(); 当通过 new class1()获得对象 obj1时,这个对象便自动获得了属性 p1和方法 method1。 在 JavaScript 中,function 本身的定义就是类的构造函数,结合前面介绍过的对象的性 质以及 new操作符的用法,下面来看使用 new创建对象的过程。 (1)当解释器遇到 new操作符时便创建一个空对象; (2)开始运行 class1这个函数,并将其中的 this指针都指向这个新建的对象; (3)因为当给对象不存在的属性赋值时,解释器就会为对象创建该属性,例如在 class1 中,当执行到 this.p1=s这条语句时,就会添加一个属性 p1,并把变量 s的值赋给它,这样 函数执行就是初始化这个对象的过程,即实现构造函数的作用; (4)当函数执行完后,new操作符就返回初始化后的对象。 通过这整个过程,JavaScript中就实现了面向对象的基本机制。由此可见,在 JavaScript 中,function的定义实际上就是实现一个对象的构造器,是通过函数来完成的。这种方式的 缺点是: l 将所有的初始化语句、成员定义都放到一起,代码逻辑不够清晰,要实现复杂的功 能时往往力不从心。 l 因为每创建一个类的实例,都要执行一次构造函数。所以实际上构造函数中定义的 属性和方法总被重复的创建,例如: this.method1=function(){ alert(“this is a test method”); } 这里的 method1每创建一个 class1的实例,都会被创建一次,造成了内存的浪费。下面 一节,将介绍另外一种类定义的机制:prototype 对象,可以解决构造函数中定义类成员带 来的缺点。 6.3.2 使用 prototype对象定义类成员 上一节介绍了类的实现机制以及构造函数的实现,现在介绍另一种为类添加成员的机 制:prototype 对象。当 new 一个 function 时,该对象的成员将自动赋给所创建的对象,例 如: <script language="JavaScript" type="text/javascript"> <!-- //定义一个只有一个属性 prop的类 function class1(){ this.prop=1; } //使用函数的 prototype属性给类定义新成员 class1.prototype.showProp=function(){ alert(this.prop); } //创建 class1的一个实例 var obj1=new class1(); //调用通过 prototype原型对象定义的 showProp方法 obj1.showProp(); PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 15 //--> </script> 因为 prototype是一个 JavaScript对象,所以可以为 prototype对象添加、修改、删除方 法和属性。从而为一个类添加成员定义。 了解了函数的 prototype对象,现在再来看 new的执行过程。 (1)创建一个新的对象,并让 this指针指向它; (2)将函数的 prototype对象的所有成员都赋给这个新对象; (3)执行函数体,对这个对象进行初始化的操作; (4)返回(1)中创建的对象。 和上一节介绍的 new的执行过程相比,现在是多了用 prototype来初始化对象的过程, 这也和 prototype 的字面意思相符,它是所对应类的实例的原型。这个初始化过程发生在函 数体(构造器)执行之前,所以可以在函数体内部调用 prototype 中定义的属性和方法,例 如: <script language="JavaScript" type="text/javascript"> <!-- //定义一个只有一个属性 prop的类 function class1(){ this.prop=1; this.showProp(); } //使用函数的 prototype属性给类定义新成员 class1.prototype.showProp=function(){ alert(this.prop); } //创建 class1的一个实例 var obj1=new class1(); //--> </script> 和上一段代码相比,这里在 class1的内部调用了 prototype中定义的方法 showProp,从 而在对象的构造过程中就弹出了对话框,显示 prop属性的值为 1。 需要注意,原型对象的定义必须在创建类实例的语句之前,否则它将不会起作用,例如: <script language="JavaScript" type="text/javascript"> <!-- //定义一个只有一个属性 prop的类 function class1(){ this.prop=1; this.showProp(); } //创建 class1的一个实例 var obj1=new class1(); //在创建实例的语句之后使用函数的 prototype属性给类定义新成员,只会对后面创建的对象有效 class1.prototype.showProp=function(){ alert(this.prop); } //--> </script> 这段代码将会产生运行时错误,显示对象没有 showProp方法,就是因为该方法的定义 是在实例化一个类的语句之后。 由此可见,prototype对象专用于设计类的成员,它是和一个类紧密相关的,除此之外, prototype还有一个重要的属性:constructor,表示对该构造函数的引用,例如: PDF 文件使用 "pdfFactory" 试用版本创建 à www.fineprint.cn 16 function class1(){ alert(1); } class1.prototype.constructor(); //调用类的构造函数 这段代码运行后将会出现对话框,在上面显示文字“1”,从而更容易看出一个 prototype 是和一个类的定义紧密相关的。实际上:class1.prototype.constructor===class1。 6.3.3 一种 JavaScript类的设计模式 尽管前面介绍了如何定义一个类,如何初始化一个类的实例,但既可以在 function定义 的函数体中添加成员,又可以用 prototype 定义类的成员,代码显的很混乱,和面向对象语 言类的实现之间有着很大的区别。那么,如何以一种清晰的方式来定义类呢?下面给出了一 种类的实现模式,并将它们对应到面向对象语言类的实现上。 类的构造函数用来初始化一个实例,是每个类必不可少的一部分。在传统意义的面向对 象中,类的构造函数的名称和类的名称一致,同时它们的定义方式和类成员的定义是类似而 又相互独立的。而在 JavaScript中,由于对象的灵活的性质,在构造函数中也可以为类添加 成员,在增加灵活性的同时,也增加了代码的复杂度。为了提高代码的可读性和开发效率, 完全可以抛弃这种定义成员的方式,而使用 prototype 对象来替代,这样 function 的定义就 是类的构造函数,符合传统意义类的实现:类名和构造函数名是相

该用户的其他资料

  • 名称/格式
  • 评分
  • 下载次数
  • 资料大小
  • 上传时间

用户评论

0/200
    暂无评论
上传我的资料

相关资料

资料评价:

/ 56
所需积分:1 立即下载
返回
顶部
举报
资料
关闭

温馨提示

感谢您对爱问共享资料的支持,精彩活动将尽快为您呈现,敬请期待!