JavaScript语言特性.docx
《JavaScript语言特性.docx》由会员分享,可在线阅读,更多相关《JavaScript语言特性.docx(11页珍藏版)》请在冰豆网上搜索。
![JavaScript语言特性.docx](https://file1.bdocx.com/fileroot1/2023-1/31/f34abea9-81aa-4491-ab68-b52c6c78ef49/f34abea9-81aa-4491-ab68-b52c6c78ef491.gif)
JavaScript语言特性
跨越边界:
JavaScript语言特性
研究编程语言中的“丑小鸭”
JavaScript常被人们认为是编程语言中无足轻重的一员。
这种观点的形成可以“归功”于其开发工具、复杂且不一致的面向HTML页面的文档对象模型以及不一致的浏览器实现。
但JavaScript绝对不仅仅是一个玩具这么简单。
在本文中,BruceTate向您介绍了JavaScript的语言特性。
几乎每个Web开发人员都曾有过诅咒JavaScript的经历。
这个备受争议的语言受累于其复杂的称为文档对象模型(DOM)的编程模型、糟糕的实现和调试工具以及不一致的浏览器实现。
直到最近,很多开发人员还认为Javascript从最好的方面说是无可避免之灾祸,从最坏的方面说不过是一种玩具罢了。
然而JavaScript现在开始日益重要起来,而且成为了广泛应用于Web开发的脚本语言。
JavaScript的复苏使一些业界领袖人物也不得不开始重新审视这种编程语言。
诸如Ajax(AsynchronousJavaScript+XML)这样的编程技术让Web网页更加迷人。
而完整的Web开发框架,比如ApacheCocoon,则让JavaScript的应用越来越多,使其不只限于是一种用于制作Web页面的简单脚本。
JavaScript的一种称为ActionScript的派生物也推动了Macromedia的Flash客户端框架的发展。
运行在JVM上的实现Rhino让JavaScript成为了Java™开发人员所首选的一类脚本语言(参见参考资料)。
我的好友兼同事StuartHalloway是Ajax方面的专家,曾在其教授的JavaScript课程中做过这样的开场白:
“到2011年,JavaScript将被公认为是一种拥有开发现代应用程序所需的一整套新特性的语言”。
他继而介绍说JavaScript程序要比类似的Java程序紧密十倍,并继续展示了使其之所以如此的一些语言特性。
在这篇文章中,我将带您探究JavaScript的一些特性,看看这些特性如何让它如此具有吸引力:
高阶函数:
一个高阶函数可以将函数作为参数,也可以返回一个函数。
此特性让JavaScript程序员可以用Java语言所不能提供的方法来操纵函数。
动态类型:
通过延迟绑定,JavaScript可以更准确和更灵活。
灵活的对象模型:
JavaScript的对象模型使用一种相对不常见的方式进行继承——称为原型——而不是Java语言中更常见的基于类的对象模型。
您可能已经熟悉动态类型模型、高阶函数形式的函数式编程以及开放对象模型这些概念,因为我在其他的跨越边界系列文章中已经作过相关的介绍。
如果您从未进行过任何正式的JavaScript开发,您很可能会认为这些特性属于非常复杂的语言,例如Python、Lisp、Smalltalk和Haskell,而绝非像JavaScript这样的语言所能提供的。
因此,我将用实际的代码示例来说明这些概念。
立即开始
您无需设置JavaScript。
如果您可以在浏览器中阅读此篇文章,就证明您已经准备就绪了。
本文包含的所有编程示例都可以在大多数浏览器内运行。
我使用的是Firefox。
用在和标记之间所包含的JavaScript加载简单的Web页面。
清单1可以显示Hello,World文本:
清单1.Hello,world
alert('Hello,World.')
要运行此代码,只需创建一个名为example1.html的文件。
将清单1的代码复制到该文件内,并在浏览器中加载此文件(参看下载部分以获得本文使用的所有示例HTML文件)。
注意到每次重载此页面时,该代码都会立即执行。
alert是个函数调用,只有一个字符串作为参数。
图1显示了由清单1中的代码弹出的警告框,显示文本“Hello,World”。
如果代码在HTMLbody之内(目前并未指定任何body,但浏览器能接受不规则的HTML,并且整个页面都默然作为一个body被处理)。
页面一旦加载,JavaScript就会立即执行。
图1.Hello,world
如果要延迟执行,可以在HTML
元素声明JavaScript函数,如清单2所示:
清单2.延迟执行
functionhello(){
alert('Hello,World.')
}
SayHello
将清单2中的代码输入到一个HTML文件,在浏览器内加载该文件,单击SayHello按钮,结果如图2所示:
图2.延迟执行
高阶函数
从清单2,可以大致体会到一些JavaScript在操纵函数方面的能力。
将函数名称传递给HTMLbutton标记并利用HTML的内置事件模型。
使用JavaScript时,我会经常在变量或数组中存储函数(在本文后面的对象模型一节,您会看到JavaScript对象模型策略大量使用了此技巧)。
例如,查看一下清单3:
清单3.用变量操纵函数
hot=functionhot(){
alert('Sweat.')
}
cold=functioncold(){
alert('Shiver.')
}
functionswap(){
temp=hot
hot=cold
cold=temp
alert('Swapped.')
}
Hot
Cold
Swap
函数是JavaScript中的一类对象,可以自由地操纵它们。
首先我声明两个函数:
hot和cold。
并分别在不同的变量存储它们。
单击Hot或Cold按钮会调用对应的函数,生成一个告警。
接下来,声明另一个函数用来交换Hot和Cold按钮的值,将此函数与第三个按钮关联,该按钮显示如图3所示的告警:
图3.操纵函数
这个例子说明可以像处理其他变量一样处理函数。
C开发人员很容易将此概念看作是函数指针功能,但JavaScript的高阶函数的功能更为强大。
该特性让JavaScript程序员能够像处理其他变量类型一样轻松处理动作或函数。
将函数用作函数的参数,或将函数作为值返回,这些概念属于高阶函数的领域。
清单4对清单3做了一点点修改,显示了能返回函数的高阶函数:
清单4.高阶函数
functiontemperature(){
returncurrent
}
hot=functionhot(){
alert('Hot.')
}
cold=functioncold(){
alert('Cold.')
}
current=hot
functionswap(){
if(current==hot){
current=cold
}else{
current=hot
}
}
Temperature
Swap
这个例子解决了一个常见问题:
如何将更改中的行为附加到用户接口事件?
通过高阶函数,这很容易做到。
temperature高阶函数返回current的值,而current又可以有hot或cold函数。
看一下这个有些陈旧的函数调用:
temperature()()。
第一组括号用于调用temperature函数。
第二组括号调用由temperature返回的函数。
图4显示了输出:
图4.高阶函数
高阶函数是函数式编程的基础,对比面向对象编程,函数式编程代表了更高级别的抽象。
但JavaScript的实力并不仅限于高阶函数。
JavaScript的动态类型就极为适合UI开发。
动态类型
通过静态类型,编译器可以检查参数和变量的值或针对一个给定操作所允许的返回值。
其优势是编译器可以做额外的错误检查。
而且静态类型还可以为诸如IDE这样的工具提供更多信息,带来其他一些特性,比如更好的代码完成功能。
但静态类型也存在着如下一些劣势:
必须提前声明意图,这常常会导致灵活性降低。
例如,更改一个Java类就会更改类的类型,因而必须重新编译。
对比之下,Ruby允许开放的类,但更改一个Java类还是会更改类的类型。
要实现相同的功能,必须输入更多的代码。
例如,必须用参数形式包括进类型信息,必须用函数形式返回值和所有变量的类型。
另外,还必须声明所有变量并显式地转化类型。
静态语言的编译-部署周期要比动态语言的部署周期长,尽管一些工具可被用来在某种程度上缓解这一问题。
静态类型更适合用于构建中间件或操作系统的语言中。
UI开发常常需要更高的效率和灵活性,所以更适合采用动态类型。
我深知这种做法存在危险。
相信使用过JavaScript的Web开发人员都曾经为编译器本应检测到的错误类型的变量而绞尽脑汁。
但它所带来的优势同样不可否认。
下面将举例加以说明。
首先,考虑一个对象的情况。
在清单5中,创建一个新对象,并访问一个不存在的属性,名为color:
清单5.引入一个属性
blank_object=newObject();
blank_object.color='blue'
alert('Thecoloris'+blank_object.color)
当加载并执行此应用程序时,会得到如图5所示的结果:
图5.引入属性
JavaScript并不会报告blue属性不存在的错误。
静态类型的拥护者大都会被本例所吓倒,因为本例中的错误被很好地隐匿了。
虽然这种做法多少会让您感觉有些不正当,但您也不能否认它巨大的诱惑力。
您可以很快引入属性。
如果将本例和本文之前的例子结合起来,还可以引入行为。
记住,变量可以保存函数!
所以,基于动态类型和高阶函数,您可以在任何时候向类中引入任意的行为。
可以轻松地重写清单5,使其如清单6所示:
清单6.引入行为
blank_object=newObject();
blank_object.color=function(){return'blue'}
alert('Thecoloris'+blank_object.color())
从上例可以看出,在JavaScript的不同概念之间可以如此轻松地来回变换,其含义上的变化很大——比如,是引入行为还是引入数据——但语法上的变化却很小。
该语言很好的延展性是它的一种优势,但同样也是其缺点所在。
实际上,该语言本身的对象模型就是JavaScript延展程度的一种体现。
对象模型
到目前为止,您应该对JavaScript有一个正确的评价了,它绝非只如一个玩具那么简单。
事实上,很多人都使用过其对象模型创建过极为复杂、设计良好的面向对象软件。
但对象模型尤其是用于继承的对象模型又非您一贯认为的那样。
Java语言是基于类的。
当构建应用程序时,也同时构建了可以作为所有对象的模板的新类。
然后调用new来实例化该模板,创建一个新对象。
而在JavaScript中,所创建的是一个原型,此原型是一个实例,可以创建所有未来的对象。
现在先暂且放下这些抽象的概念,去查看一些实际代码。
比如,清单7创建了一个简单的Animal,它具有name属性和speak动作。
其他动物会从这个基础继承。
清单7.创建一个构造函数
Animal=function(){
this.name="nobody"
this.speak=function(){
return"WhoamI?
"
}
}
myAnimal=newAnimal();
alert('Theanimalnamed'+myAnimal.name+
'says'+myAnimal.speak());
清单7的结果如图6所示:
图6.创建一个构造函数
对于Java开发人员而言,清单7中的代码看起来多少有点生疏和奇怪。
实际上对于没有亲自构建过对象的许多JavaScript开发人员来说,这些代码同样看起来有点生疏和奇怪。
也许,下面的解释可以让大家能够更好地理解这段代码。
实际上,您只需重点关注其中三段信息。
首先,JavaScript用嵌套函数表示对象。
这意味着清单7中的Animal的定义是一种有效的语法。
第二,JavaScript基于原型或现有的对象的实例来构造对象,而非基于类模板。
funct()是一种调用,但newAnimal()却基于Animal内的原型构造一个对象。
最后,在JavaScript中,对象只是函数和变量的集合。
每个对象并不与类型相关,所以可以自由地修改这种结构。
回到清单7。
如您所见,JavaScript基于在Animal中指定的原型定义一个新对象:
myAnimal。
继而可以使用原型中的属性和函数,甚或重定义函数和属性。
这种灵活性可能会让Java开发人员受不了,因为他们不习惯这种行为,但它的确是一种十分强大的模型。
现在我还要更深入一步。
您还可以使用名为prototype实例变量来指定对象的基础。
方法是设置prototype实例变量使其指向继承链的父。
如此设置prototype之后,您所创建的对象会为未指定的那些对象继承属性和函数。
这样一来,您就可以模仿面向对象的继承概念。
以清单8为例:
清单8.通过原型继承
Animal=function(){
this.name="nobody"
this.speak=function(){
return"WhoamI?
"
}
}
Dog=function(){
this.speak=function(){
return"Woof!
"
}
}
Dog.prototype=newAnimal();
myAnimal=newDog();
alert('Theanimalnamed'+myAnimal.name+
'says'+myAnimal.speak());
在清单8中,创建了一个Dog原型。
此原型基于Animal。
Dog重定义speak()方法但却不会对name()方法做任何改动。
随后,将原型Dog设置成Animal。
图7显示了其结果:
图7.通过原型继承
这也展示了JavaScript是如何解决到属性或方法的引用问题的:
JavaScript基于原始的原型创建实例,该原型在构造函数中定义。
任何对方法或属性的引用都会使用所生成的原始副本。
您可以在对象内像定义其他任何变量一样重新定义这些变量。
这样做必然会更改此对象。
所以您显式定义的任何属性或函数都将比在原始的原型中定义的那些属性或函数优先级要高。
如果您显式设置了名为prototype的实例变量,JavaScript就会在此实例中寻找任何未定义的实例变量或属性。
这种查找是递归的:
如果在prototype内定义的实例不能找到属性或函数,它就会在其原型中查找,依此类推。
那么,JavaScript的继承模型到底是什么样的?
这取决于您如何对它进行定义。
您需要定义继承行为以便可以覆盖它。
然而,从本质上讲,JavaScript更像是一种函数式语言,而非面向对象的语言,它使用一些智能的语法和语义来仿真高度复杂的行为。
其对象模型极为灵活、开放和强大,具有全部的反射性。
有些人可能会说它太过灵活。
而我的忠告则是,按具体作业的需要选择合适的工具。
结束语
JavaScript对象模型构建在该语言的其他功能之上来支持大量的库,比如Dojo(参见参考资料)。
这种灵活性让每个框架能够以一种精细的方式更改对象模型。
在某种程度上,这种灵活性是一种极大的缺点。
它可以导致可怕的互操作性问题(尽管该语言的灵活性可以部分缓解这些问题)。
而另一方面,灵活性又是一种巨大的优势。
Java语言一直苦于无法充分增强其灵活性,原因是它的基本对象模型还未灵活到可以被扩展的程度。
一个典型的企业级开发人员为能够成功使用Java语言必须要学习很多东西,而新出现的一些优秀的开放源码项目和新技术,比如面向方面编程、Spring编程框架和字节码增强库,则带来了大量要学的代码。
最后,JavaScript优秀的灵活性的确让您体会到了一些高阶语言的强大功能。
当然您无需选择为每个项目或大多数项目都做这样的权衡和折衷。
但了解一种语言的优势和劣势——通过参考大量信息,而不仅仅基于广告宣传或公众意见——会让您可以更好地控制何时需要使用以及何时不能使用这种语言。
当您在修改JavaScriptWeb小部件时,您至少知道该如何让此语言发挥它最大的优势。
请继续跨越边界吧。
参考资料
学习
您可以参阅本文在developerWorks全球站点上的英文原文。
JavaToRuby:
ThingsEveryManagerShouldKnow(PragmaticBookshelf,2006年):
本文作者所著的书,介绍了何时何地可以从Java编程转换到RubyonRails,以及如何进行转换。
BeyondJava(O'Reilly,2005年):
本文作者所著的书,介绍了Java的兴起和兴盛,以及能够在某些领域挑战Java平台的技术。
Scriptaculous和Prototype:
两种JavaScript框架,通过RubyonRails助力Ajax。
ObjectHierarchyandInheritanceinJavaScript:
有关JavaScript继承的很好资源。
Ajaxian:
这个Ajax的门户网站上有所有有关Ajax的杰出讨论,极大地推动了JavaScript的复苏。
Dojo:
一个JavaScript开放源码工具箱。
Rhino:
JVM上的JavaScript引擎。
用函数式编程技术编写优美的JavaScript(developerWorks,2006年7月):
作者解释了如何使用JavaScript(TM)™(JavaScript能导入函数式编程的构造和特性)编写优美的代码。
Java技术专区:
数百篇Java编程各方面的文章。
Ajax技术资源中心:
获得developerWorks上有关Ajax的所有信息。
关于作者
BruceTate是位父亲、山地车手、皮艇手,住在德克萨斯州的奥斯汀。
他是三本最畅销Java图书的作者,包括获得Jolt奖的Better,Faster,LighterJava。
他最近推出了FromJavatoRuby和Rails:
UpandRunning。
他在IBM工作了13年,而后创立了RapidRed顾问公司,在那里他专攻基于Ruby的轻量级开发策略和体系结构及RubyonRails框架。
现在他是WellGoodLLC的CTO,该公司致力于为非盈利组织和福利机构开辟市场。