JavaScript学习指南(第2版)(修订版)

978-7-115-29633-7
作者: 【美】Shelley Powers
译者: 谢春祥
编辑: 陈冀康

图书目录:

详情

本书系统地介绍了JavaScript的基本语法、基本对象、调试工具与排错技术、事件处理机制、浏览器对象模型/文档对象模型(bom/dom)等方面的知识,并通过一个复杂的示例深入探讨了Ajax的应用。

图书摘要

版权信息

书名:JavaScript学习指南(第2版)(修订版)

ISBN:978-7-115-29633-7

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [美] Shelley Powers

  译    谢春祥

  责任编辑 陈冀康

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


Copyright©2009 by O’Reilly Media, Inc.

Simplified Chinese Edition, jointly published by O’Reilly Media, Inc. and Posts & Telecom Press, 2009. Authorized translation of the English edition, 2009 O’Reilly Media, Inc., the owner of all rights to publish and sell the same.

All rights reserved including the rights of reproduction in whole or in part in any form.

本书中文简体版由O’Reilly Media, Inc.授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式复制或抄袭。

版权所有,侵权必究。


本书系统地介绍了JavaScript的基本语法、基本对象、调试工具与排错技术、事件处理机制、浏览器对象模型/文档对象模型(BOM/DOM)等方面的知识,并通过一个复杂的示例深入探讨了Ajax应用。本书提供了许多简单易懂、主题鲜明的示例,介绍了大量最佳实践和良好编程习惯,对提高代码可读性、可维护性均有很高的价值,并且对很多跨浏览器兼容问题进行了详细说明,追踪了新规范的发展。

本书适合于希望通过JavaScript为自己的网页/网站添加活力的读者,不管你是否有编程经验,通过阅读本书都能够很快地掌握这一技术。在阅读本书之前,最好对CSS、HTML/XHTML有所了解。


Shelley Powers在Web技术领域的工作和写作经历已超过13年,涉及的内容从JavaScript的第一个版本到最先进的图像处理和网页设计工具。她最近在O’Reilly出版社出版的书籍涵盖语义Web、Ajax、JavaScript和Web图像等主题。她还是狂热的业余摄影师,同时也是Web开发的狂热爱好者,喜欢将她最新的体验应用到许多网站上。


本书的封面动物是一只黑色、钩嘴的小犀牛(非洲黑犀牛)。这只黑色犀牛是两种非洲犀牛中的一种,其体重约1.5吨,比白色的或方嘴的犀牛体型小一些。这种黑犀牛生长在非洲西南部、中南部和东部的热带草原、空旷丛林或山地森林中。它们喜欢独居,会积极地捍卫自己的领地。

这种黑犀牛的上嘴唇是尖细的,像一个钩子,能够很方便地从树木或灌木丛上采摘树叶、嫩枝和枝芽。它比其他草食动物更擅于食取粗大的植物。

黑色犀牛是奇趾动物,每只脚上有3只脚趾。它拥有很厚的、灰色的、无毛的表皮。和其他犀牛相比,最明显的不同是它有两个触角,触角实际上不仅是骨头,还有浓密的毛发。这种犀牛通过自己的触角来对抗狮子、老虎、土狼等敌人,或者用来寻找自己的配偶。其求爱的过程通常是非常暴力的,它的触角可能会给对方带来很严重的伤害。

在交配之后,公母犀牛就不会有进一步的联系。其怀孕周期通常是14~18个月,虽然小犀牛一出生几乎马上就能够自己吃食物,但母犀牛仍会再看护它一年。在小犀牛离家独立生活之前,通常会和它的妈妈待在一起4年左右。

在近几年中,犀牛被大量屠杀以至于濒临灭绝。科学家估计非洲在100年前差不多有100万头黑犀牛,而现在已经减少到2400多头左右。而且现存的5种犀牛(包括印第安犀牛、爪哇犀牛和苏门答腊岛犀牛)都濒临灭绝。人类是最大的掠食者。


O’Reilly Media通过图书、杂志、在线服务、调查研究和会议等方式传播创新知识。自1978年开始,O’Reilly一直都是前沿发展的见证者和推动者。超级极客们正在开创着未来,而我们关注真正重要的技术趋势——通过放大那些“细微的信号”来刺激社会对新科技的应用。作为技术社区中活跃的参与者,O’Reilly的发展充满了对创新的倡导、创造和发扬光大。

O’Reilly为软件开发人员带来革命性的“动物书”;创建第一个商业网站(GNN);组织了影响深远的开放源代码峰会,以至于开源软件运动以此命名;创立了Make杂志,从而成为DIY革命的主要先锋;公司一如既往地通过多种形式缔结信息与人的纽带。O’Reilly的会议和峰会集聚了众多超级极客和高瞻远瞩的商业领袖,共同描绘出开创新产业的革命性思想。作为技术人士获取信息的选择,O’Reilly现在还将先锋专家的知识传递给普通的计算机用户。无论是通过书籍出版,在线服务或者面授课程,每一项O’Reilly的产品都反映了公司不可动摇的理念——信息是激发创新的力量。

业界评论

“O’Reilly Radar博客有口皆碑。”

——Wired

“O’Reilly凭借一系列(真希望当初我也想到了)非凡想法建立了数百万美元的业务。”

——Business 2.0

“O’Reilly Conference是聚集关键思想领袖的绝对典范。”

——CRN

“一本O’Reilly的书就代表一个有用、有前途、需要学习的主题。”

——Irish Times

“Tim是位特立独行的商人,他不光放眼于最长远、最广阔的视野并且切实地按照Yogi Berra的建议去做了:‘如果你在路上遇到岔路口,走小路(岔路)。’回顾过去Tim似乎每一次都选择了小路,而且有几次都是一闪即逝的机会,尽管大路也不错。”

——Linux Journal


JavaScript最初的设计意图是为了在浏览器端(当时就是Netscape Navigator)中载入的Web页面和位于服务器上的应用程序之间提供脚本化的接口。JavaScript早在1995年就出现了,因此它已经发展成为Web开发的关键组件,当然我们也能看到它在其他领域的应用。

本书的主题就是JavaScript语言,内容包括从该语言开始就有的最基本的数据类型,到它最复杂的特性(包括那些与Ajax和动态页面效果有关的功能)。当你读完本书之后,你将掌握应用最高级的库和Web应用程序的基础知识。

本书假设读者熟悉Web页面技术,包括CSS和HTML/XHTML。你可以没有任何编程经验,但如果是这样,有些章节你可能需要多读几遍。

本书对以下读者将有帮助:

如前所述,本书将假设你在HTML和CSS方面有一定经验,同时对Web应用程序的工作机制有基本的理解。编程经验并不是必要的,不过由于本书涉及JavaScript的方方面面,因此有些部分可能会比较复杂。虽然很复杂的内容并不多,但如果要使用较新的Ajax库,还是需要对JavaScript有足够的理解。

使用JavaScript是一件很有挑战性的事情,因为应用程序不仅将运行在不同类型的计算机上,还将运行在不同的浏览器上。如果仔细研究网站的Web服务器日志文件,就可以访问诸如Firefox 3.0或IE 8.0之类的现代浏览器,以及诸如IE 5.0之类的早期浏览器。

当然也可以尝试创建能够应对不同操作系统和浏览器的JavaScript程序,不过更好的选择是根据访问Web页面的用户最常用的浏览器,选择一组目标浏览器,然后使用这些浏览器来测试应用程序。你可能会发现应用程序无法在旧版本浏览器上正常工作,不过也不需要针对所有人对所有环境都提供支持。

本书谈及JavaScript某部分的工作原理时,可能会提及“目标浏览器”。对于本书而言,针对的目标浏览器是Firefox 3.x、Opera 9.x、Safari 3.x(包括WebKit的最新版本,它是Safari的基础结构),以及主要的IE 8.0(IE的下一个版本)。IE的绝大多数的示例在IE 7.x和IE 6.x中也都能够正常运行,如果不能正常运行,本书会做一些说明。以下是可以获取这些浏览器的URL列表。

JavaScript和浏览器的发展过程是动态的,这也为编写一本关于JavaScript的书带来了特殊的挑战。虽然我尽力在本书中包含了JavaScript的最新更新,但JavaScript规范(更准确地说,应该是ECMAScript规范)和浏览器都在不断地发生重大变化。例如,在本书处于编辑阶段时,ECMAScript工作组宣布了不再继续研发JavaScript 2项目,转而关注新的临时规范版本——ECMAScript 3.1。不过,新的ECMAScript规范中所带来的变化在许多目标浏览器上不会马上得以实现。当然我确信这一规范中引入的功能将会在浏览器的后续版本中陆续实现,因此我在本书中添加了一些“提示”,简要说明这些即将来临的变化。

另外,浏览器开发商也在一直发布新版本的工具。本书中用来测试示例的目标浏览器所展现的状态是我写书时的表现情况,当你阅读本书时运行结果应该不会受到太大影响。

不过,我关注的绝大多数素材都是“传统”(classic)的JavaScript,它不仅稳定,而且是所有浏览器和脚本语言修改时所依托的平台。本书中的绝大多数(虽然不是所有)示例都能够在老版本和后续版本的浏览器(以及用来测试这些示例的目标浏览器)中正常工作。

祝你好运。

本书是由6个相对独立的部分组成的。

第1~3章概述了JavaScript应用程序的结构,包括该语言支持的简单数据类型,以及基本语句和控制结构。该部分将帮助你对后续章节中使用的语言建立最基本的理解。

第4章和第5章介绍了JavaScript中的主要对象,即String、Number和Boolean,以及其他内置对象,如Math、RegExp(针对正则表达式)、Array,还有所有重要的函数。

第6章的讨论暂时离开了语言本身,介绍了一些浏览器调试工具和故障排除技术,为阅读本书后续章节中更复杂的脚本示例做准备。

第7章介绍了事件处理机制,第8章详细描述了表单事件和带表单的JavaScript应用程序,进一步探讨事件处理机制。

第9~11章深入研究了Web页面开发中更复杂的部分。这些章节介绍了浏览器对象模型(Browser Object Model,BOM)和更新的文档对象模型(Document Object Model,DOM),同时说明了如何创建自定义对象。如果要创建新窗口,或者单独访问、修改甚至动态创建任何页面元素,理解这些模型是十分重要的。另外,通过使用自定义对象,可以实现语言或浏览器内置功能之外的功能。这些章节还介绍了浏览器中的cookie以及更高级的客户端存储技术。

第12~15章研究了JavaScript的高级应用,包括动态页面效果和Ajax,并且详细地介绍了XML和JavaScript对象表示法(JavaScript Object Notation,JSON)在Ajax应用程序中的应用。

虽然我希望在介绍JavaScript时遵循一条合理的线索,但有时仍然会在示例中用到一些必须在后续章节中才会详细介绍的功能。当出现这种情况时,我会注明在哪些章节中会提供更进一步的说明。

以下对本书各章的内容做简要的描述。

第1章“Hello JavaScript!”

该章简要介绍了JavaScript语言,同时分析了一个小型的简单Web页面应用程序。该章还讲解了与使用JavaScript相关的要点,包括针对JavaScript应用程序推荐的良好编程实践。

第2章“JavaScript数据类型和变量”

该章概述了JavaScript中的基本数据类型,同时简要介绍了其变量、标识符以及JavaScript语句的结构。

第3章“运算符和语句”

该章介绍了JavaScript中的基本语句,包括赋值语句、条件分支语句及控制语句,同时还介绍了所需的各类运算符。

第4章“JavaScript对象”

该章介绍了3种基本的JavaScript对象,包括Number、String和Boolean,以及Date和Math对象。该章还介绍了RegExp对象,它是用来实现模式匹配的关键对象。

第5章“函数”

该章重点介绍了JavaScript中内置的函数这一重要的对象。函数是创建自定义对象的关键,也是将JavaScript代码块封装成可复用功能的基础,这样在应用程序中就可以多次调用这些代码块。

第6章“排错、调试及跨浏览器问题”

该章简要介绍了本书中各种目标浏览器(IE、Safari、Firefox和Opera)所提供的调试环境,同时探讨了跨浏览器开发方面的基本问题。

第7章“捕获事件”

该章关注事件处理机制,内容包括事件处理的基本表单(它在许多应用程序中仍然很常用),以及更新的基于DOM的事件处理。

第8章“表单、表单事件及验证”

该章介绍了JavaScript在表单和表单字段方面的应用,包括如何访问各种类型的表单字段,如文本输入域、下拉列表等,以及如何对表单域的数据进行验证。在表单提交给Web服务器之前,对其数据进行验证有助于避免出现不必要的服务器通信,从而节省时间和资源。该章还简要地说明一些与安全和表单相关的话题。

第9章“浏览器之谜”

该章介绍了通过JavaScript访问页面时需要使用的对象模型。首先是浏览器对象模型(BOM),它是层次结构的对象,包括窗口、文档、表单、历史记录、位置等对象。通过BOM,JavaScript能够打开新窗口,访问如表单、链接、图像等页面元素,甚至还能够创建一些基本的动态效果。

第10章“cookie和其他客户端存储技术”

该章介绍了基于脚本的cookie,用来在客户端的计算机上存储少量的数据。通过cookie,可以保存用户名、密码及其他相关信息,这样用户就不必每次输入相同的数据。另外,该章还概要地介绍了一些新出现的客户端存储技术,如Google的Gears和HTML 5.0中的本地存储,它们都提供了比cookie更强的功能。该章还回顾了JavaScript沙箱方面的知识。

第11章“DOM或以树型展示的Web页面”

该章关注DOM,它是一个直观、重要的对象模型,提供了访问所有文档元素和属性的机制。虽然该模型十分全面,也十分直观易懂,但该章对于新程序员而言仍然存在很多挑战。

第12章“动态页面”

该章概述了动态修改Web页面的方法,包括修改某个元素的样式,以及在页面上添加或删除元素。该章还研究了如拖放、页面区域的展开/折叠、可见性、移动等效果。阅读本章之前,需要对CSS有基本的理解。

第13章“创建自定义JavaScript对象”

该章展示了在JavaScript中创建自定义对象的方法,介绍了JavaScript中启用此类结构的原型(prototype)结构。在此,还介绍了一些关于编程语言的概念,如继承、封装,不过即使没有理解这些概念,也可以从该章中获益。

第14章“使用Ajax”

该章介绍了Ajax,虽然它能够创建令人兴奋的效果,但实际上并不是一个很复杂的JavaScript应用。该章剖析一个复杂的示例,该示例包含服务器端代码。

第15章“Ajax数据:XML或JSON”

在第14章中用来演示Ajax功能的示例是基于HTML片段完成数据交换的,在该章中将说明如何在Ajax应用程序中生成和处理XML,然后再说明如何用JSON完成相同的任务。我们会介绍这些技术的优点,并说明何时应该使用哪种技术。

本书中使用了以下排版约定:

表示是命令行的输入、要逐字输入的选项、C#关键字或代码示例;

在语法或代码中表示可替换的项,如变量、可选元素等;

用于强调程序中的一段代码;

用来表示路径名、文件名、互联网地址(如域名、URL)以及新定义的术语。

提示

 

表示技巧、建议或提示。

 

警告

 

表示警告和告诫。

本书的目标是帮你完成自己的工作。一般来说,你可以自由地在自己的程序和文档中使用本书中的代码,无须向我申请授权,除非要复制本书的大部分代码。例如,在编写程序时使用了本书中的几段代码是无须申请授权的。但销售或发布O’Reilly图书中示例的CD-ROM就需要申请授权了。在回答别人问题时,引用本书中的文字和示例代码也无须申请授权。将本书中的大量示例代码放到你的产品文档中就需要申请授权了。

我们重视但不强制使用引用说明。引用说明通常包括书名、作者、出版社和ISBN,例如:“Learning JavaScript,Second Edition,by Shelley Powers. Copyright 2009 Shelley Powers, 978-0-596-52187-5。”

如果你感觉在使用这些示例代码时超出了正当权限,或者超出了这里的许可,可以发邮件到permissions@oreilly.com与我们联系。

当你在最喜欢的技术书籍的封面上看到Safari联机丛书的图标时,这意味着本书可以通

过O’Reilly Network Safari BookShelf在线获得。

Safari提供了一个比电子书更好的解决方案。它是一个虚拟图书馆,允许你轻松搜索成千上万本高端技术书籍,剪切和粘贴代码示例,下载样章,并且在你需要更加准备和获取当前信息时快速查找答案。可以在http://safari.oreilly.com免费试用它。

对于本书中的信息,我们已经尽最大努力进行了测试和校验,但你仍然可能会发现一些特性已经发生了变化(甚至是我们自身的错误)。如果你发现任何不妥之处,或者对后续版本有任何建议,请按以下联系方式与我们联系。

美国:

O’Reilly Media, Inc.

1005 Gravenstein Highway North

Sebastopol, CA 95472

中国:

100080北京市西城区西直门成铭大厦C座807室

奥莱利技术咨询(北京)有限公司

如果想询问一些技术问题,或者对本书做些评论,请发邮件到:

bookquestions@oreilly.com

本书还有一个专门的Web页面,上面有本书的示例代码以及后续版本的写作计划。可以通过以下网址访问这些信息:

http://www.oreilly.com/catalog/9780596521875

关于本书、相关会议、资源中心以及O’Reilly Network相关的信息,请访问O’Reilly网站(http://www.oreilly.com)。

我想感谢我的编辑和技术审校团队,包括负责本书技术审校的技术编辑Tony Ruscoe、Jeni Tennison、Matthew Russell和Trey Holdener,还有和我长期合作的编辑Simon St.Laurent,你们的努力使本书变得更好。另外,我还得感谢出版团队的其他成员:Rachel Monaghan、Sumita Mukherji、Joe Wizda和Jessamyn Read。


JavaScript之所以如此流行,一个重要的原因是JavaScript能相对轻松地与网页完美集成。编写JavaScript代码,只需要在网页中添加一个script元素,将type属性指定为“text/javascript”,然后写入希望的JavaScript代码:

<script type="text/javascript">
...some JavaScript
</script>

使用JavaScript不需要安装任何额外组件,也没有库路径配置等烦琐的工作。JavaScript可以直接在大多数Web浏览器上运行,如Firefox、IE、Opera及Safari。要做的只是添加一个脚本化的块,写入代码,仅此而已。

传统上,将JavaScript代码块添加到文档(由网页中的开始和结束head标记限定)元素中,而事实上,JavaScript代码也可以放在body元素中,甚至可以同时在head元素和body元素中添加JavaScript代码。然而,在body元素中添加JavaScript代码并不是最佳实践,这种做法往往会给以后阅读、修改JavaScript带来麻烦。唯一的好处是可以解决性能问题,第6章将介绍如何解决这种问题。在本书的所有示例中,仅把脚本化代码块添加到网页的head元素中。

在学习一门新语言时,第一个示例通常都是编写“Hello World”程序,也就是一个向用户界面输出字符串“Hello,World!”的程序。对于JavaScript而言,“Hello World”程序则是要在网页中显示“Hello,World!”。如示例1.1所示,网页中的JavaScript代码块只有一行JavaScript代码,弹出一个警告对话框,并显示“Hello, World!”。

示例1.1 最简单的JavaScript程序“Hello, World!”

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 
Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Hello, World!</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script type="text/javascript">

alert("Hello, World!");

</script>
</head>
<body>
</body>
</html>

将示例1.1的代码复制到文件中,并在浏览器中打开该文件,就能看到显示“Hello, World!”的警告对话框。如果没看到该对话框,请检查浏览器是否启用了JavaScript。

提示

 

如果通过“文件”下面的“打开”菜单,而不是采用网页地址,例如http://<somedomain. com>/index.html,老版本的IE浏览器也会自动禁用脚本。

尽管这个程序的功能非常有限,但是通过该程序或多或少可以了解构成JavaScript程序的最小组件构成:网页、script元素及JavaScript代码。尝试自己修改JavaScript代码,将“World”替换成你的名字。

如果希望在浏览器中除了输出静态的消息,还想更进一步实现其他功能,那么请看第二个示例。

和第一个“Hello World!”程序不同,第二个“Hello World!”程序是在网页中输出消息。为此,该程序用到了JavaScript程序的4个重要组件:浏览器内置的document对象、JavaScript变量、JavaScript函数及事件处理程序,如示例1.2所示。

示例1.2 第二个“Hello World!”程序

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Hello, World!</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script type="text/javascript">
function hello() {

  // 向世界问好
  var msg = "Hello, World!";
  document.open();
  document.write(msg);
  document.close();
}
</script>
</head>
<body onload="hello()">
<p>Hi</p>
</body>
</html>

尽管示例1-2是一个小型应用程序,但是它已经涵盖了大多数JavaScript应用程序的基本组件。本章余下的内容将详细地介绍JavaScript程序中的每一个组件。

提示

 

本章并不打算介绍在示例1.1和示例1.2中使用的文档类型声明(DOCTYPE),它会对浏览器处理JavaScript代码的方式有些影响。第6章将介绍DOCTYPE的影响。

JavaScript经常在其他语言的上下文中使用,例如在HTML及XHTML等标记语言中使用。然而,JavaScript并不能随意地直接添加到这些标记语言中的任何地点。

在示例1.2中,JavaScript代码封装在script元素中,所以当浏览器读取到script元素时,就不会以HTML或XHTML的方式处理其内容,而是通知浏览器的脚本引擎来接管script元素中的内容。

嵌入网页中的脚本并不一定都是JavaScript,script元素的type属性定义了脚本类型。在本示例中的脚本类型是text/javascript,其他可能的属性值是:

第一种可选值ecmascript表示以ECMAScript方式(基于ECMA-262脚本标准)解释这段脚本。第二种可选值jscript表示以JScript方式(微软在IE浏览器中所实现的ECMAScript语言的一种变种)解释这段脚本。最后两种可选值表示以微软的VBScript方式处理,这是一种完全不同的脚本语言。

所有这些可选type值都表示内容是基于MIME编码的。MIME是一种标识内容如何编码及其指定格式的方式。基于MIME编码,那些浏览器就可以处理这种类型,而其他浏览器跳过该段代码,从而确保只有处理该脚本的应用程序才能访问脚本。

早期的script标签中还有language属性,用来表示语言的版本及类型,如javascript 1.1或javascript 1.2。然而,在HTML 4.01标准中已经弃用了language属性,不过在许多JavaScript示例中还是能够看到language属性,这也是一种早期的跨浏览器技术。

提示

 

跨浏览器表示JavaScript可以在所有的目标浏览器上运行,或者消除不同浏览器之间的差异,本书称为JavaScript程序可以跨浏览器使用。

几年前,在解决跨浏览器兼容问题时有一种常见的做法,那就是为不同的浏览器创建相应的script元素,并引用不同的文件或代码,然后使用language属性确保只有兼容的浏览器才能够访问该段代码。下面是在1997年编写的一个古老示例:

<script src="ns4_obj.js" language="javascript1.2">
</script>
<script src="ie4_obj.js" language="jscript">
</script>

这种方法的工作原理是,如果浏览器支持JavaScript 1.2(如Netscape Navigator 4.x),那么就会执行ns4_obj.js文件中的代码;而如果浏览器支持JScript(如IE 4.0),那么将执行ie4_obj.js文件中的代码。你可能觉得这种做法很诡异,的确是这样,不过这在当时的确解决了动态脚本的跨浏览器的动态页面兼容问题。

script的其他有效属性包括src、defer及charset。charset属性定义了脚本的字符编码集。一般情况下不需要设置charset,除非脚本需要采用与文档完全不同的字符编码集。

另外一个有用的属性是defer。如果将defer属性设置为“defer”,那么表示该脚本不会生成任何文档内容,于是浏览器可以提前处理页面的剩余部分,在页面处理结束并做好显示准备时才处理脚本部分。

<script type="text/javascript" defer="defer">
...no content being generated
</script>

defer属性可以提高页面载入的速度,特别是那些引用了大量的JavaScript代码或者庞大的JavaScript程序库的页面。

最后一个src属性用来表示所需要载入的外部JavaScript文件。不过下面将首先介绍text/javascript类型属性,以及它在不同浏览器中的含义。

为文档的body添加脚本

前面我们曾经说过,因为对script元素进行集中管理有利于网页的可维护性,所以script元素通常将添加到网页上的head元素中。然而,在body元素中添加脚本的原因往往是出于性能的考虑。

因为浏览器从同一个域名并发载入的资源是有限制的,所以当把脚本添加到head元素中时,首先载入的将是脚本,其次才是文档的剩余部分。此外,浏览器可能会延迟页面剩余部分的显示,因为脚本中可能会调用document.write方法修改document对象。如果JavaScript文件很庞大,那么网页中的图片以及其他重要的信息将会延迟显示,这所带来的问题远比可维护性更加重要。

即使在script元素中使用defer属性也不一定能完全解决该问题,特别是并发资源访问加载和页面显示的限制。

High Performance Web Sites(中译版《高性能网站建设指南》)一书中,作者Steve Souders推荐将script元素放在文档的最末尾处,这样网页的其他部分就可以优先载入。大多数复杂Web应用程序网站的开发人员更倾向于这种方法。把脚本放在页面的底部带来的负面影响是脚本不容易查找,网页的可维护性也较差。

那么什么才是最佳方法呢?我发现大多数网站并不包含庞大的JavaScript库,这些JavaScript库如此庞大,以至于脚本放置成了一个问题,当为了确保页面易于维护时, 更加不会包含庞大的JavaScript库。不过,如果的确需要使用复杂的JavaScript库,那么可以考虑将脚本放在页面的最末尾处。

不论采用何种方法,请确保脚本位置的一致性,要么全部放在head元素中,要么全部在body元素的最末尾处。

示例1.2中的script元素的类型是text/javascript,该程序可以在Firefox、IE、Opera以及Safari中正常运行。然而,并不是所有的浏览器都实现了JavaScript。

尽管“JavaScript”已经成为浏览器客户端脚本的代名词,但是只有Mozilla和流行的Mozilla浏览器Firefox实现了JavaScript语言,后者是广义的脚本规范ECMAScript的一个实例的名称。ECMAScript实际是行业内的客户端脚本规范,它的最新的版本是ECMA-262第3版。

然而,大多数浏览器都支持text/javascript类型,除此之外还兼容text/ecmascript(尽管不常见),只不过不同浏览器或者其他应用程序对这两种类型的支持存在着一些差异。

提示

 

ECMAScript并不局限于浏览器,Adobe的ActionScript(Flash中的脚本语言)也是基于ECMA-262第3版的。

本书中所有程序都在Firefox 3.x、Safari 3.x、Opera 9.x以及IE 8.0中做过测试,虽然它们对ECMAScript-262第3版提供了基本支持,但并不是全部,而且ECMAScript也已经有了下一代——ECMAScript 3.1。本书还将介绍各种浏览器之间的差异以及跨浏览器的解决方案。同时也将使用大家最为熟悉的text/javascript作为script元素的类型属性,如示例1.2所示。

示例1.2中的部分JavaScript代码创建了用来显示“Hello, World!”消息的函数hello。函数是包含一行或者多行脚本的一种方式,它可以重复运行多次。函数也可以用来控制何时执行所包含的脚本。例如,示例1.2只在页面载入后调用该函数。

下面是创建函数的典型语法:

function functionname(params) {
  ...
}

首先是关键字function,后面紧跟着函数名、圆括号,圆括号中包含零个或多个参数(函数参数)。示例1.2中的JavaScript函数没有参数,然而在本书中的其他示例里就可以看到大量带参数的函数。函数的主体脚本则放在大括号中。

之所以说这是函数的“典型”语法,是因为它并不是创建函数的唯一语法。然而,从第5章开始会遇到其他变体,第5章将对JavaScript函数做更详细的介绍。

当然,有了函数后,还需要调用函数才能执行函数内的脚本,这样也就引出了事件处理程序。

在示例1.2中的body元素里面,将hello函数赋值给一个名为onload的HTML属性。onload属性就是众所周知的事件处理程序。这个事件处理程序和其他的事件处理程序是每个浏览器提供的底层对象模型的一部分。

事件处理程序可以将函数映射到某个特定事件,当事件触发时,就会执行该函数中的脚本。最常见的一种事件处理程序就是body元素的onload事件,当网页载入结束时就会触发该事件,事件处理程序也就将调用映射的函数。

下列是一些常见的事件处理程序:

这些只是事件处理程序中的一小部分而已,并且不是所有元素都支持所有的事件处理程序。例如,只有一部分HTML元素支持onload事件处理程序,如body和img元素;不要大惊小怪,因为事件是与某种资源的载入相关联的。

在开放元素中直接添加是添加事件处理程序的一种方法。还有一种方法是直接在JavaScript中添加代码,语法如下:

<script type="text/javascript">
window.onload=hello;

function hello() {

  // 向世界问好
  var msg = "Hello, World!";
  document.open();
  document.writeln(msg);
  document.close();
}
</script>

onload事件处理程序是浏览器内置对象window的一个属性。在脚本的第一行中,将函数hello赋给了window对象的onload事件处理程序。

提示

 

在JavaScript语法中,JavaScript函数也是对象,所以可以通过名字或直接将函数赋给一个变量或另一个对象的属性。

基于对象属性的方法,就无须将事件处理程序作为属性添加到元素标记中,而是可以在JavaScript代码中直接添加。第7章将更加详细地介绍事件处理程序及其高级用法。现在,我们先简单看看document对象。

示例1.2尽管很简短,但是它使用了浏览器中功能最强大的对象之一——document对象。document对象的所有目的是呈现整个页面,包括页面中的所有元素。通过document对象可以访问页面中的所有内容,正如你所见,基于document对象也可以修改页面的内容。

document对象还包含映射到页面元素的集合,如页面中所有的图像或窗体元素。document对象还提供了访问及修改网页的方法,如示例1.2中所使用的open、writeln及close方法。

open方法可以打开要修改的HTML页面。在示例1.2中,JavaScript脚本打开了包含脚本的同一个文档。writeln方法是write方法的变种,后者可以输出文本字符串到页面中。write和writeln方法之间的唯一区别是writeln在输出文本之后会自动添加换行符。close方法用来关闭文档,并强制浏览器立即呈现文档内容。

当页面载入后,向现有文档中连续写入新内容会使页面之前的内容被擦除。这也就是为什么打开页面后在页面中只看到“Hello, World!”消息,而看不到“Hi”的原因。

警告

 

在IE浏览器(IE 8.0 beta版)中,如果向现有文档中连续写入新内容会引发另一个问题,即导致“后退”按钮丧失其功能。

在示例1.2中,open和close方法并不是必需的方法,因为在页面载入之后,浏览器在调用writeln方法时会自动打开、关闭文档。如果是在页面正文中使用该脚本,那么就需要显式地调用open方法。

document对象和之前所提及的window对象一样,都是浏览器内置对象结构(也就是浏览器对象模型,即BOM)的一部分。BOM是大多数现代浏览器中实现的基本对象集合。第9章将详细介绍document对象以及其他BOM对象。

提示

 

BOM是更为正式的文档对象模型(DOM)的前身,有时称为DOM Level 0。

在示例1.2中,通过JavaScript语言中的其中一个property操作符(.)就可以访问document对象中的方法。

JavaScript支持的操作符有许多,包括算术运算符(+和−)、条件表达式(<和>)以及稍后会介绍到的其他运算符。在这些操作符中,最重要的一种类型就是属性操作符。数据元素、事件处理程序以及对象方法在JavaScript语言中都被看做是对象的属性,都可以通过property操作符访问。

property操作符还可以通过称为方法链(有时简称链)的方式进行调用,在同一条语句中,可以逐个地调用多个方法。在本书中你将会看到这样的示例:

var tstValue = document.getElementById("test").style.backgroundColor 
="#ffffff";

在该示例中,通过document的getElementById方法访问page元素,然后通过访问style对象来设置元素的背景色。backgroundColor是style对象的一个属性,style对象是page元素的一个属性,而page元素则可以通过getElementById方法访问,getElementById又是document对象的一个属性。

后续章节还会介绍这些方法和对象,现在我们只是了解一下方法链的调用方式。如果要返回对象的所有属性,则不能使用方法链,只有返回单个对象时才可以。

提示

 

在目前最流行的一个Ajax库jQuery中就大量应用了方法链。后续的章节将会简要地介绍jQuery库。

在示例1.2中,把字符串“Hello, World!”赋给了msg对象,后者是个JavaScript变量的示例。变量仅仅是对一个数据的命名引用。该数据可以是示例1.2中的这样一个字符串,也可以是一个数字或者布尔值true或false,还可以是函数引用、数组或者另一个对象。

在该示例中,关键字var用于定义变量。如果使用var定义变量,那么该变量就是一个局部变量,也就是只能在定义变量的函数内使用。如果不使用var,那么msg变量就将是全局变量,可以在函数内外访问该变量。在局部上下文使用全局变量不一定是坏事,在某些情况下也的确需要这样用,然而这并不是一种好的实践,应当尽可能避免这样的用法。

之所以要避免使用全局变量,是因为如果应用程序是大型JavaScript应用程序的一部分,那么就可能在另一个文件中的某段代码里使用到msg变量,这样就会覆盖该文件中原有的数值。或者,如果已经有了,而由于误用了var关键字,某些其他库的脚本就会覆盖全局变量msg,并且导致数据丢失。

变量的作用域是非常重要的,特别是当全局变量与局部变量同名的时候。虽然在示例1.2中并没有使用全局变量,但是从一开始就保持良好的JavaScript编程实践是非常重要的。

变量作用域的规则如下:

只要在函数内使用var关键字定义变量,就可以避免全局变量和局部变量同名的问题。特别是使用如Dojo、jQuery以及Prototype之类的JavaScript库时,因为程序员并不想要了解其他JavaScript代码所使用的变量名。

JavaScript语言中还提供了处理不同指令的不同语句。在示例1.2中使用了最基本的一种JavaScript语句——赋值语句,它用来为变量赋值。其他的语句包括:for循环语句,基于给定的计数值循环地处理代码块;if-else条件语句,根据条件判断是否执行某段代码块;switch条件判断语句,在给定的集合当中进行判断,执行与值相关联的代码段等其他语句。

每种类型的语句都有特定的语法要求。在示例 1.2 中,赋值语句的结尾是个分号。JavaScript并不强制要求用分号作为语句的结束符(除非希望在同一行输入许多语句)。如果一行当中包含许多语句,那么就需要通过分号来分割不同的语句。

当在一行中输入一条完整的语句时,可以使用换行符语句的结束符。然而,和使用var一样,最好使用分号作为语句的结束符,即使没有原因,这样做也能够提高代码的可读性。第3章将介绍与分号、其他运算符和语句相关的更多内容。

正如本章希望阐述的那样,即使在示例 1.2 这样的小型应用程序中也包含了许多JavaScript细节。接下来将介绍JavaScript的注释。

注释往往是对其后代码的总结或解释。JavaScript语言中的注释,是解释代码段的含义以及代码依赖性的一种有用方法。注释往往能够提高代码的可读性和可维护性。

注释有两种不同的类型。第一种类型是使用双斜杠,所有的注释必须在同一行,例如:

//在代码中注释掉该行
var i = 1; // 这是该行中的注释

第二种类型是使用JavaScript注释分隔符,/*和*/,用来标记超过一行或多行的注释,例如:

/ *这是一条多行注释, 
它一共占用了三行。
在注释一个函数时,多行注释特别有用*/

相对来说,使用单行注释比较安全,因为在不小心删除左大括号或者右大括号时,多行注释可能会引发问题。

一般来说,在一段脚本执行特定的进程或者创建特定的对象之前,使用单行注释;而在JavaScript文件的开始处,使用多行注释。在每个JavaScript代码块、函数或者对象定义的开始处,至少添加一行注释是一种良好的代码规范。此外,在所有JavaScript程序库文件的开头,都应该提供更为详细的注释块;注释块应该包含作者、日期、依赖性以及脚本的详细用途等信息。

到目前为止,我们已经对示例1.2中的JavaScript代码逐一做了详细的描述。现在我们就来查看在示例1.2中未涉及的其他细节。

10年前,当大多数浏览器还处于第1版或者第2版时,大家对JavaScript的支持情况参差不齐,不同的浏览器中有不同的实现。当浏览器(例如基于文本的Lynx)遇到script标记的时候,浏览器通常将脚本内容直接输出到页面上。

为了避免这样的问题,脚本内容通常会放在HTML注释(<!--和-->)中,这样即使是没有启用JavaScript的浏览器,也会直接注释掉该段脚本,但是新一代的浏览器则知道要执行该段脚本。

虽然这是一种不正规的用法,但是它广为流传。拥有JavaScript当前特性的大多数网页以新增的HTML注释为特色,因为往往会复制脚本。遗憾的是,现在一些新的浏览器能够以XHTML方式解释网页,甚至是XML的方式,这就会忽略所注释的代码段。在这些情况下,就会忽略JavaScript,而不会执行JavaScript。所以,不推荐使用HTML注释来“隐藏”脚本。

然而,推荐另一种“隐藏”脚本的方式,那就是使用XML CDATA区段(section),特别是在XHTML文档中使用脚本时。示例1.3在示例1.2的基础上做了一些修改,新增了CDATA区段,修改的部分已加粗显示。

示例1.3 示例1.2的修改版,添加CDATA区段来“隐藏”脚本

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Hello, World!</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script type="text/javascript">
//<![CDATA[

function hello() {
  var msg = "Hello, <em>World!</em>";
  document.open();
  document.write(msg);
  document.close();
}

//]]>
</script>
</head>
<body onload="hello()">
<p>Hi</p>
</body>
</html>

之所以使用CDATA区段是因为:XHTML处理器会解释标记,如新示例中的em元素开始标记和结束标记,即使标记包含在JavaScript字符串中。虽然脚本可以正确地处理并且可以正确地显示页面,但是如果不使用CDATA区段验证脚本,就可能导致验证错误,如图1.1所示。

图1.1 不使用CDATA区段的验证错误

假设通过script元素的src属性在页面中导入JavaScript,是XHTML标准所兼容的,并且不要求使用CDATA区段。尽管应该通过CDATA限定内联或者嵌入的JavaScript,特别是包含在body元素中的JavaScript。对于大多数浏览器而言,还需要用JavaScript注释符(//)来隐藏CDATA区段开始和结束标记,如示例1.3所示,否则将会出现JavaScript错误。

当然,保持页面整洁的最佳方式是将JavaScript代码从页面中彻底移除,改成使用链接JavaScript文件的方式。

在本书的大多数示例中,JavaScript代码是直接嵌入在页面里的,这样可以提高代码的可读性且易于检查。然而,Mozilla基金会推荐将所有内联或者嵌入的JavaScript代码都从页面中移除,放在独立的JavaScript文件中。使用独立的JavaScript文件(参见下一节),可以避免校验以及文本解释错误等问题,而不用担心页面是以HTML还是XHTML方式进行处理。

提示

 

使用JavaScript文件往往也能提高网页载入的效率,因为浏览器会在第一次载入文件的时候进行缓存,引用相同文件时则会从缓存中获取。

JavaScript的应用越来越基于面向对象的方式,而且更为复杂。为了简化其开发工作,并且共享自己的JavaScript代码,许多JavaScript开发人员(包括其他开发人员)创建了可复用的JavaScript对象,以便在其他应用程序中复用。共享这些对象的唯一有效的方法,就是在单独的文件中创建这些对象,并在网页中提供每个文件的链接。现在具有了文件中的代码,所有开发人员所需做的只是将代码链接到自己的网页中。如果以后需要修改代码,那么只需要在一个地方进行修改即可。

现在,几乎所有简单的JavaScript代码都将放在独立的脚本文件中。然而使用多个JavaScript文件所导致的系统开销也可能多于其带来的好处。在网页中引用JavaScript库或者脚本文件的语法如下所示:

<script type="text/javascript" src="somejavascript.js"></script>

script元素中没有任何内容,但是需要以</script>作为结束标记。

浏览器会依照脚本文件在页面中出现的顺序,依次在页面中载入它们,并依次处理每个脚本文件,使用defer的情况除外。处理脚本文件的方式应该与代码实际包含在页面中一样,这种行为在脚本文件和嵌入的JavaScript代码块之间完全一致。

示例1.4是“Hello World!”程序的另一个修改版本,区别在于其将脚本从页面中移到了独立的文件中,该文件命名为helloworld.js。独立的JavaScript文件必须以js作为文件后缀名,除非Web服务器能够将其他后缀名识别成JavaScript MIME类型。然而,因为js后缀名已经作为默认文件扩展名使用多年,所以没有必要特意去破坏这样的约定。

提示

 

当然,任何规则都有例外的情况,js后缀名也是如此。如果JavaScript文件是由服务器端应用程序内置语言(如PHP)动态生成的,那么该文件会有不同的后缀名。

示例1.4是独立的JavaScript文件,而示例1.5则是引用该JavaScript文件的网页。

示例1.4 “Hello World!”脚本位于独立的JavaScript文件中

/* 
  函数名:hello
  作者:Shelley
  hello函数将输出"Hello, World!"
*/

function hello() {

  // 向世界问好
  var msg = "Hello, <em>World!</em>";
  document.open();
  document.write(msg);
  document.close();
}

示例1.5 调用外部脚本文件的网页

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Hello World!</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script type="text/javascript" src="helloworld.js"> 
</script>
</head>
<body onload="hello()">
<p>Hi</p>
</body>
</html>

现在的网页比之前干净整洁多了,并且应用程序的可维护性也更好。其他应用程序也可以复用这段JavaScript代码。虽然复用“Hello World!”这样的代码是不太可能的,但是在本书稍后介绍的示例中能够体现出复用的重要性。

在第2章开始介绍变量和数据类型之前,下面还是先学习本章的最后一节。

如果有理想的世界,那么开发人员都希望访问自己网站的用户使用相同的操作系统、相同的浏览器,并且都启用了JavaScript。用户不会使用移动电话或者其他奇怪的设备,视力不好的人也不需要使用屏幕朗读设备,听力不好的人也不需要语音导航设备。

然而理想总归是理想,现实往往与之不一样,许多JavaScript开发人员都需要考虑这些现实的情况。我们如此痴迷于我们可以创造什么产品的奇迹之中,以至于我们忘记了并非每个人都可以共享这些产品。

许多最佳实践都是与JavaScript相关的,但最重要的实践有这么一条:任何JavaScript功能都不应该成为网站和访问者之间的障碍。

什么是“网站和访问者之间的障碍”呢?JavaScript不应当阻碍那些没有启用JavaScript的用户正常访问网站。如果基于JavaScript创建了下拉框菜单,那么同时也应该为没有启用JavaScript的用户提供非基于JavaScript的替代选项。如果访问站点的用户视力不佳,那么当动态地往页面添加指令的时候,也应该考虑对语音浏览器的支持。

许多开发者并不遵循这些最佳实践,因为这需要做更多的工作。然而这并不应该成为负担,因为这样能够增加站点的可访问性。此外,现在的许多公司都期望自己网站的可访问性达到一定程度。当然,应该从一开始就养成创建高可访问性页面的习惯,而不是每次去解决页面可访问性的问题。

关于如何创建高可访问性的JavaScript应用,在WebAIM网站(http://www.webaim.org)上能够找到一份很详尽的指南(http://www.webaim.org/techniques/javascript)。该指南介绍了在哪些情况下应该避免使用JavaScript,如使用JavaScript创建菜单及其他导航。然而,该指南也介绍了如何利用JavaScript来提高站点的可访问性。

本书的建议是判断这些事件是否能由鼠标触发。例如,与其只捕获鼠标单击事件,还不如捕获键盘或鼠标所触发的事件,如onfocus和onblur事件。如果有一个下拉菜单,那么应该添加一个独立的页面,然后在另一个页面上提供静态的菜单。

阅读了WebAIM上的指南之后,还可以再看看W3C网站中关于可访问性的介绍(http://www.w3.org/WAI),另外还可以浏览一下美国政府的Section508网站(http://www.section508.gov),后者讨论众所周知的“508一致性”。无论物理约束与否,都可以访问遵循Section 508的网站。在那些网站上,你可以访问各种工具,这些工具评价网站的可访问性,如Cynthia Says (at http://www.cynthiasays.com/).

当然,在默认情况下,在仅限于或者禁用了JavaScript的浏览器中,并不是所有可访问性的问题都跟那些浏览器有关,如遇到有屏幕朗读设备的情况。许多人并不信任JavaScript,或者不喜欢或者无意中禁用了JavaScript功能。对于两组人——那些不喜欢使用JavaScript的用户或者没有选择的用户,当没有脚本存在时提供替代选项是非常重要的,如noscript。

一些浏览器或其他应用程序并不支持JavaScript,或者受限于其解释脚本的方式。如果JavaScript对于导航或用户交互不重要,那么浏览器忽略脚本不会带来任何问题。然而,如果JavaScript对于访问网站的资源必不可少,而同时又不提供任何替代选项,那么用户就会离你而去。

很多年前,当JavaScript还是一个新事物的时候,一种流行的方法是提供另一份简单的或者纯文本的页面,可以通过一个链接访问页面,通常将链接放置在页面的上方。然而,这种维护两个网址的做法并不推荐,开发人员经常会担忧保持两个网址同步的问题。

另外,一种更好的方法是为脚本生成的动态内容提供静态的替换选项。当使用JavaScript创建下拉菜单时,同时也应该提供一个标准的层次结构链接的菜单;当用户使用script基于用户交互提供用于编辑的表单元素时,在另一个页面上提供更多传统的链接来实现同样的功能。

启用所有这一切功能的元素是noscript元素。不论在什么地方需要静态的内容,都可以在开始和结束标签中通过内容添加noscript元素。如果浏览器或其他应用程序不能处理脚本(因为某些原因禁用了JavaScript),那么就会选择noscript的内容;如果浏览器支持JavaScript,那么就会忽略noscript中的内容。

示例1.6是“Hello World!”程序的最后一个版本,它在之前版本的基础上(受CDATA保护的示例)添加了noscript元素。在启用JavaScript的浏览器中可以看到一个页面和输出的“Hello World!”,如果在禁用JavaScript功能的浏览器中,则会显示不一样的消息结果。

示例1.6 使用noscript元素来支持禁用JavaScript的浏览器

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Hello, World!</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script type="text/javascript">
//<![CDATA[

function hello() {
  //向世界问好
  var msg = "Hello, <em>World!</em>";
  document.open();
  document.write(msg);
  document.close();
}

//]]>
</script>
</head>
<body onload="hello()">
<noscript>
<p>I'm still here, World!</p>
</noscript>
</body>
</html>

当然,示例1.6只介绍了noscript的简化用法;本书稍后章节还会介绍更为复杂的示例,以及可选的脚本安全的方法。

要对示例1.6进行测试,可以使用Firefox中名为Web Developer Toolbar(Web开发工具条)的扩展选项。其工具栏中有个选项能够禁用JavaScript支持。当启用JavaScript时,会显示原始的“Hello World!”信息。不过,如果禁用JavaScript支持,就会显示“I’m still here, World!”消息。虽然在浏览器中也可以禁用脚本,不过使用类似Web Developer Toolbar的开发工具简化了测试。

你喜欢开发的浏览器决定了你所能使用的工具。本书推荐基于Firefox进行开发,这样就可以广泛使用Web Developer Toolbar以及Firebug(一个神奇的调试工具)了。后续章节还将介绍如何排除故障和调试,以及可用于其他浏览器的工具和选项。


JavaScript中的变量是已命名的数据段,是一种为数据创建引用的方式,无论数据是字符串、数字、布尔值、数组或者其他对象,都可以通过变量名多次访问该数据。更重要的是,变量可以用于在不同的过程中持久化数据。例如,JavaScript应用可以将窗体元素的值保存在变量中,然后就能够通过变量访问该值,而不必每次都从窗体元素中获取。

变量的数据类型是JavaScript脚本引擎对变量中所保存数据的类型解析。字符串变量用来保存字符串,数字变量用来保存数字,以此类推。不过,JavaScript和其他语言不同,在同一个应用程序中,相同的变量可以保存不同类型的数据。这就是俗称的松散类型(loose typing)或动态类型,这意味着JavaScript中的变量在不同时候可以根据不同的上下文保存不同的数据类型。在支持松散类型的语言中,声明变量时无须指定变量的类型是字符串、数字,还是布尔类型,因为在处理应用程序的过程中变量的数据类型将是动态决定的。如果某个之前用来保存字符串的变量现在要作为数字来使用,只要其字符串中的内容的确能够表示为数字,而不是如电子邮件地址之类的内容,那么也是没有问题的。如果还想继续将其当成字符串来使用,也一切正常。

然而松散类型的特性也会引发一些麻烦。如果希望对两个数字执行加法操作,但是JavaScript引擎将保存其中一个数字的变量解释成了字符串数据类型,那么其结果就将是连接这两个字符串,而不是求这两个数字的和。在JavaScript语言中,上下文将决定变量的数据类型。

本章将介绍JavaScript的基本数据类型,包括字符串、数字和布尔值,以及修改这些类型对应值的内置函数。此外,还将介绍JavaScript中两种特殊的数据类型null和undefined(未定义)。最后还将说明字符串的转义以及Unicode编码。此外,本章还将深入介绍变量,以及如何创建有效和有意义的变量标识符。

JavaScript变量涉及变量名、作用域以及特定的数据类型。因为JavaScript语言支持松散类型,所以可以对变量做任意的修改。

JavaScript变量与其他编程语言大致相同,都是用来保存值的,并且都能够在代码中的不同地方显式地访问对应值。每个变量都有一个标识符,而且在同一范围内只能存在一个同名的标识符。变量名由字母、数字、下划线和$符号组成。JavaScript标识符对格式没有特定的要求,但要求以字母、$或下划线开头,例如:

_variableidentifier
_variableidentifier
variableIdentifier
$variable_identifier
var_ident[1]

从JavaScript 1.5开始,也可以在变量名中使用Unicode编码中的字母(如ü)和数字,以及转义序列(例如\u0009)。下面几个也是合法的JavaScript变量标识符:

_üvalid
T\u0009

使用特殊字符时要特别注意,因为某些工具(如调试器)对特殊字符的支持可能会存在一些问题。

JavaScript是区分大小写的,也就是说大写字符和小写字符是不一样的。例如,JavaScript语言会将下面两个变量标识符识别为两个不同的变量:

stringVariable
stringvariable

对于变量标识符还有一个限制,它们不能是JavaScript关键字(如表2.1所示)。随着JavaScript新版本(如ECMAScript)的推出,可能还会引入新的关键字。

表2.1 JavaScript关键字[2]

break else new typeof
case false null var
catch for switch void
continue 2function this while
default if throw with
delete in true
do instanceof try

根据ECMA-262标准的相关扩展,表2.2中列出的单词也被认为是关键字,因而也不能用于变量标识符。

表2.2 ECMA-262标准的保留字

abstract enum int short
boolean export interface static
byte extends long super
char final native synchronized
class float package throws
const goto private transient
debugger implements protected volatile
double import public

除了ECMAScript保留字之外,大多数浏览器中实现的与JavaScript相关的单词也被认为是实现的保留字。许多保留字都是源于浏览器对象模型(Browser Object Model,BOM)的,如document和window对象(第1章已经简要介绍过)。表2.3列出了这种情况中最常见的单词,不过它并不完整。

表2.3 浏览器定义的典型保留字

alert eval location open
array focus math outerHeight
blur function name parent
boolean history navigator parseFloat
date image number regExp
document isNaN object status
escape length onLoad string

除了上一节讨论的变量命名的限制之外,还可以在代码中使用变量和函数的任何标识符,但是有些命名规范源于Java或其他编程语言,有助于提高代码的可阅读性和可维护性。

命名时应该使用有意义的单词,而不要随便命名。例如,interestRate这样的变量标识符要比intRt或ir更加具有描述性。即使在给定的上下文中,后两种变量名太生僻和难懂,容易造成阅读代码的困难。

当然也可以将数据类型作为变量名的一部分,例如一个用来保存名字的字符串变量,可以命名为:

var strFirstName = "Shelley";

这种将数据类型作为变量名一部分的命名约定,也被称为匈牙利命名法,在Windows开发中非常流行。在过去的JScript应用程序中,经常会看到这样的命名方式,然而这种命名方式在现在的JavaScript开发中并不常见。

另一种命名约定是对集合变量采用复数形式命名:

var customerNames = new Array();

一般来说,变量名的首字母不应采用大写字母,因为首字母大写通常用来表示对象,如String类:

var firstName = String("Shelley");

对对象名的首字母使用大写,有助于区分简单的变量。

对于函数名和变量名,通常首字母都是小写的,而函数名将以动名词形式表示,可以让读者更容易通过函数名了解函数的功能,例如:

function validateNameInRegister(firstName,lastName) ...

在大多数情况下,变量名和函数名都是由一个或多个单词拼接而成,并遵循骆驼拼写法(CamelCase,每一个单词的首字母小写,后续单词的首字母大写),这也是其他编程语言所流行的命名法。下面就是一些遵循骆驼拼写法的变量名:

validateName
firstName

骆驼拼写法有助于提高代码的可读性,当然变量“单词”中的下划线[3]也能起到相同的作用,例如:

Validate_name

在最新的JavaScript程序库中也大量使用了骆驼拼写法,这也正是本书推荐这种命名法的原因。

尽管变量名的首字母可以是$、下划线、字母[4]等,但是最好的选择还是用字母作为首字母。在变量名中不必要地使用不常见的符号,会使代码变得晦涩难懂,特别是对于那些JavaScript新手而言。然而,如果看过新的JavaScript库或者示例,就会发现在这些代码中也存在一些奇怪的变量名。基于Ajax的流行Prototype JavaScript库Prototype就是典型代表,所以我认为这可能会引发“Prototype效应”的新命名约定。

下面就是一个使用这种命名约定的变量名示例:

var _break = someval;

在这些JavaScript库中,以下划线为首字母的变量表示它是对象的私有数据成员(第13章将详细介绍对象的成员变量)。Prototype引入的另一个有意思的命名约定是,对于返回页面元素引用的函数使用$符号作为函数名称,例如:

$('test').invokeSomeMethod();

下划线或$符号的这类相对新颖的用法,并不会改变变量的行为,它仅仅是一种命名方式。

提示

 

关于JavaScript库Prototype的详细介绍参见http://www.prototypejs.org/

本节主要介绍了JavaScript的命名限制,这些命名约定并没有什么强制性或者神奇的地方,它们仅仅是为了让JavaScript代码更容易阅读和调试。

JavaScript是一种清晰易懂的编程语言,它实现了脚本语言所需的功能,但又不显得臃肿。然而,JavaScript在某些方面的确存在一些容易引发混淆的地方。

例如,JavaScript中只有3种基本数据类型:字符串、数字以及布尔类型。每种数据类型互不相同,它们分别对应于字符串值、数字值以及布尔值。然而,JavaScript还提供了一些内置对象,如String、Number和Boolean。这些对象看起来与基本数据类型又截然不同,前3种是基本值的类型,而后3种则是对象,是拥有内置属性和方法的对象。

然而它们事实上又紧密相关。当以对象的方式使用基本类型时,String对象会封装字符串基本类型,而Number和Boolean也同样会封装各自的基本类型。当在JavaScript中创建了简单的字符串变量并使用String对象的方法时,JavaScript会隐式地通过String对象封装字符串基本类型,并且调用String对象的属性和方法,最后丢弃该对象。在接下来的代码段中,当字符串变量firstName调用toUpperCase方法的时候,JavaScript会创建一个对象来封装这个字符串,然后调用toUpperCase方法,最后丢弃这个临时对象:

var firstName = "Shelley";
var cappedName = firstName.toUpperCase();

firstName变量虽然是一个基本类型,但是却表现得像一个对象,可以像对象一样调用toUpperCase的方法。如果调用String对象的其他方法,也同样会创建一个String对象,封装字符串基本类型,调用方法,最后丢弃临时对象。所以,如果希望以对象的方式操作字符串,那么最好的方式是创建一个对象。如果只是需要用一个简单的字符串来输出消息,或者保存字符串值,而不需要对象提供的所有功能,那么创建字符串基本类型将是更好的选择。

所以,如果仍然糊涂地混用基本数据类型和对象,那么就违背了JavaScript区分基本数据类型和对象的初衷了。接下来的3节将分别介绍每种基本数据类型,说明它们是如何创建和操作的,以及如何进行数值类型转换。第4章将详细介绍数据对象,以及它们的方法和属性。

提示

 

本书所提及的“封装”一词,通常指的是为包装数据项,如String对象封装了字符串基本类型。

由于JavaScript是一门支持松散类型的编程语言,字符串变量或者数字型、布尔型变量在声明时并没有什么差别,只有把字面量值赋给字符串变量,并且对变量进行初始化之后才定义了变量的上下文。

字符串字面量是由单引号或双引号所引用的一系列字符,例如:

var strString = "This is a string";
var anotherString= 'But this is also a string';

JavaScript并未限定必须用单引号或双引号来表示字符串,唯一的规则是前后的符号必须匹配。字符串可以包括各式各样的字符,例如:

var thirdString = "This is 1 string.";
var stringFour = "This is--another string.";
var stringAsNumber = "543";

在最后一个字符串中引用的虽然是一个数字,但是由于该数字是放在双引号中的,因此JavaScript认为这是一个字符串变量。

字符串还可以包括单引号或双引号,但这个时候封装整个字符时要使用另外一种引号,而且必须保持前后一致。如果字符串包含单引号,那么就要使用双引号来表示字符串;如果需要包含双引号,那么就应使用单引号来表示字符串,例如:

var string_value = "This is a 'string' with a quote."

或者

var string_value = 'This is a "string" with a quote.'

空字符串是一种特殊情况,一般用来在定义字符串变量时初始化它。下面就是空字符串的示例:

var string_value = '';
var anotherStringValue = "";

JavaScript引擎会以统一的方式来处理表示字符串的单引号和双引号。当然,在代码中,统一使用单引号或双引号,能够提高代码的可读性。

在JavaScript语言中,并不是所有字符都可以直接放在字符串中的。字符串还可以包含转义序列,例如\n表示换行符。转义符是通过特定方式将一些特殊字符经过编码之后放在字符串中的一种模式。

下列代码将包含换行符转义序列的字符串字面量赋给某个变量。当在对话框中显示该字符串时,转义符\n会被解释为换行符:

var string_value = "This is the first line\nThis is the second line";

这时对话框中显示的结果将是:

This is the first line
This is the second line

反斜杠同样可以用来在字符串中对引号进行转义,使用\"就表示引号是字符串字面量的一部分,而不是字符串的结束符号,例如:

var string_value = "This is a \"string\" with a quote."

通过使用反斜杠,就可以在字符串中同时使用单引号和双引号。

如果要在字符串中使用反斜杠,那么应该写成双反斜杠(第一个反斜杠表示转义):

var string_value = "This is a \\string\\ with a backslash."

该字符串包含两个反斜杠,在string字符前后各有一个。

在字符串中还可以使用Unicode字符,表示方法是在\u后面加上4位的十六进制数值。例如,以下示例定义的是中文简体的“爱”字:

document.writeln("\u7231");

当然Unicode字符的显示是由浏览器所决定的,但是,大多数流行的浏览器都支持标准的Unicode编码。

提示

 

关于Unicode编码的更多内容详见http://www.unicode.org

转义符、反斜杠是非常实用的,特别是在字符串中要使用通常表示控制字符的ASCII码时。然而,反斜杠对于非ASCII码字符是无能为力的,也无法确保整个字符串在HTML处理过程中是安全的,而这些情况却又是基于Ajax的应用程序经常会面临的。

encodeURI和encodeURIComponent方法的用途就是对字符串进行转义,准确地说是对字符串进行编码,将其从ASCII码或非ASCII码字符转换成URIencoding字符。

提示

 

URI是统一资源标识符(Uniform Resource Identifier)的缩写,例如网页的URL。ISO Latin-1(或者ISO 8859-1)就是一种URI编码规范。

encodeURI方法假设字符串是一个URI,例如“http://oreilly.com”,并对下列字符进行编码:

; , / ? : @ & = + $

对于字母、数字以及下列字符不会进行编码:

- _ . ! ~ * ' ( )

对页面片段符号(#)也不会进行编码。

然而,encodeURIComponent方法会对所有的字符(除了字母和上面所列举的字符)进行编码。该方法将需要进行编码的字符串视为URI的一个参数,因此属于URI的字符也会编码,例如,下列这些字符:

# & + =

这两个函数有着相对应的解码函数:decodeURI,用来对encodeURI编码过的字符串进行解码;decodeURIComponent,用来对encodeURIComponent编码过的字符串进行解码。

示例2.1中的网页使用了这4个函数,分别对两个字符串进行编码和解码,编码和解码后的字符串都会通过document.writeln输出到页面中,如图2.1所示。

示例2.1 通过JavaScript的encodeURI和encodeURIComponent方法
对两个字符串进行URI编码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>URI Encoding</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
//<![CDATA[

function encodeStrings() {
   var sOne =
encodeURIComponent("http://burningbird.net/index.php?pagename=$1&page=$2");
   var sTwo = encodeURI("http://someapplication.com/?catsname=Zöe&URL=");
   var sOutput = "<p>Link is " + sTwo + sOne + "</p>";
   document.write(sOutput);

   var sOneDecoded = decodeURI(sTwo);
   var sTwoDecoded = decodeURIComponent(sOne);

   var sOutputDecoded = "<p>" + sOneDecoded + "</p><p>" + sTwoDecoded + "</p>";
   document.write(sOutputDecoded);

}
//]]>
</script>
</head>
<body onload="encodeStrings()">
  <p></p>
</body>
</html>

图2.1 对URI应用程序编码和解码后的输出

这些示例介绍了如何显式地创建字符串变量,以及对包含特殊字符的字符串字面量的处理。当然,也可以根据上下文,将其他数据类型转换成某个特定类型。

数字、布尔值等其他数据类型都可以转换成字符串;一般来说,脚本引擎将根据上下文自动完成这样的转换。例如,当把数字或布尔型变量传给希望接收字符串变量的函数时,就会先隐式地将该数值转换成字符串,再进行处理:

var num_value = 35.00;
alert(num_value); //预期为一个字符串

此外,如果在赋值语句中要对两个变量执行加法操作,其中一个是字符串变量,而另一个是数字变量,那么数字变量就会自动转换成字符串,接着再连接这两个字符串:

var num_value = 35.00;
var string_value = "This is a number:" + num_value;

什么时候将数字转换成字符串,取决于JavaScript脚本引擎在什么时候处理字符串。例如,如果字符串是序列值中的第一个,那么会把所有数值都当成字符串进行处理:

var strValue = "4" + 3 + 1;   // 结果是 "431"
var strValueTwo = 4 + 3 + "1"; // 结果是 71

然而,如果使用其他操作符号(除了加号),那么会将字符串转换为数字:

var firstResult = "35" - 3;  // 减法操作,结果是32
var secondResult = 30 / "3"; // 除法操作,结果是10
var thirdResult = "3" * 3;  // 乘法操作,结果是9

隐式转换取决于操作符和变量的位置,这更加充分地体现了松散类型的危险:数值会随着上下文发生变化,而这取决于引入新数据类型操作的顺序,以及所引用的操作符。

提示

 

本章介绍了加法和其他操作符,其他JavaScript操作符请阅读本书第3章。

与其完全依赖于偶然的数据类型转换,还不如自己调用String全局函数显式地执行字符串转换。如果要转换的是一个布尔值,那么结果字符串则是布尔值的文本表示,“true”表示布尔真值,而“false”则表示布尔假值。对于数字而言,转成的字符串就是表示该数字的字符串,例如,“-123.06”表示数字-123.06,当然这取决于数字的位数和精度。NaN数值(非数字值,稍后将会介绍)则会返回字符串“NaN”,而未定义或空值的变量则会返回字符串“undefined”或者“null”。

表2.4是在不同数据类型上调用String[5]函数的执行结果。

表2.4 tring[6]转换表

输入

结果

undefined

"undefined"

null

"null"

布尔值

"true"或"false"

数字

数字相应的字符串,NaN表示非数字值

字符串

不转换

对象

对象默认的字符串

在表2.4中,最后一行描述了ECMAScript中针对对象调用String函数的执行规则,其生成的默认字符串形如:

"[object "+className+"]"

示例2.2显式地对不同变量和对象进行转换,创建数字和布尔型变量,并初始化相应数据类型的数值,接着调用String函数显式地将其转换成字符串。该示例还为一个已创建但还没有初始值的变量,以及一个初始化为null的变量进行转换。最后,对document对象执行转换,并将结果输出到页面中。

示例2.2 隐式和显式的字符串转换

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Implicit and Explicit String Conversion</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
//<![CDATA[

function convertToString() {
   var newNumber = 34.56;
   var newBoolean = true;
   var nothing;
   var newNull = null;

   var strNumber = String(newNumber); var strBoolean = String(newBoolean);
   var strUndefined = String(nothing); var strNull = String(newNull);

   var strOutput = "<p>" + strNumber + " " + strBoolean + " " 
+ strUndefined + " " + strNull + "</p>";
   document.writeln(strOutput);

   var strOutput2 = String(document);
   document.writeln(strOutput2);

}
//]]>
</script>
</head>
<body onload="convertToString()">
  <p></p>
</body>
</html>

在不同的浏览器(Firefox、Opera、IE及Safari)中,该示例输出的第一个字符串都是一样的:

34.56 true undefined null

然而,只有Opera和Firefox浏览器将输出document对象的ECMAScript特定表示法:

[object HTMLDocument]

IE浏览器则仅仅显示[object],而Safari和WebKit浏览器则根本不支持将document对象转换成字符串。

Boolean(布尔)数据类型只有两种可能值:true和false。布尔值不需要使用引号,所以"false"与false所表示的含义是完全不一样的。

var isMarried = true;
var hasChildren = false;

显式布尔值可以赋给不同类型的变量,这取决于该变量是否已经赋值,以及该变量的当前值。例如,下列条件表达式将把testVariable转换为布尔型变量,如果testVariable变量值为true则执行if语句内的程序,如果testVariable变量值为false则跳过这段程序:

if (testVariable) {
  ...
}

关于如何根据布尔值决定应用程序的控制流程,将在以后的章节中介绍。但是目前,将不同类型的变量显式或隐式转换为布尔值的规则列在表2.5中。在这里所说的“显式”是指使用Boolean函数将其他数据类型(如String)的值转换为布尔值:

var someValue = 0;
var someBool = Boolean(someValue)

表2.5 oBoolean转换表

输入

结果

Undefined

False

Null

False

布尔值

布尔值

数字

如果数字等于0或NaN,那么结果是false;否则为true

字符串

如果是空字符串,那么结果为false;否则为true

对象

true

双重否定符(两个否定操作符“!”)可以用来显式地将数字或字符串转换为布尔值:

var strValue = "1";
var numValue = 0;
var boolValue = !!strValue;   // 把字符串"1" 转换为 true
boolValue = !!numValue;     // 把数字0 转换为 false

在JavaScript中,Number(数字)数据类型是浮点数,可以包含小数部分,也可以没有小数部分。如果没有小数部分,那么该数字将被当做十进制整数,取值范围为−253~253

以下变量是合法的整数:

var negativeNumber = -1000;
var zero = 0;
var fourDigits = 2534;

浮点型表示方式可以包含小数部分,小数部分位于右侧。数字也可以用指数形式(科学计数法)表示。下面列出的都是合法的浮点型数字:

var someFloat = 0.3555
var anotherNumber = 144.006;
var negDecimal = -2.3;
var lastNum = 19.5e-2  // 也就是0.195
var zeroDecimal = 12.0;

尽管JavaScript能够支持较大的数字,但是某些函数只能够支持−2e31(−2,147,483,648)到2e31(2,147,483,648)之间的数字;所以,请尽量将数字限制在这个范围内。

JavaScript的Number数据类型有两个特殊的数字:正无穷大和负无穷大,分别用Infinity和-Infinity表示。当在JavaScript应用程序中出现数学溢出的时候,就会返回正无穷大。而当所使用的数字比JavaScript所能够支持的最小数字还小时就将用到负无穷大。

尽管对于一些老浏览器而言,八进制可能会与十六进制相互混淆,但是除了十进制外,还可以使用八进制或十六进制来表示JavaScript数字。十六进制数字是以0x开头的,例如:

var firstHex = -0xCCFF;

八进制也是以0开头,但是没有前面的x,例如:

var firstOct = 0526;

我们之所以对八进制和十进制表示方式都感兴趣,是因为如果使用String函数将八进制或十进制数字转换为字符串,那么脚本引擎将首先会把数字转换为十进制数,然后再将结果转换为字符串。所以,firstOct变量转换为字符串的结果是“342”,342正是八进制数字0526转成十进制的结果。

前面两节介绍了如何将其他数据类型转换为字符串或者布尔值。本节将继续介绍两个不同的函数,分别用于将字符串转换为数字:parseInt和parseFloat。其中parseInt函数只返回数字的整数部分,无论字符串是整数还是浮点数。而parseFloat函数则会返回浮点数值,直到遇到一个字符,这个字符不是正负号、小数、数字或者指数。

示例2.3中有3个包含数字的字符串,分别调用parseInt和parseFloat函数对其进行转换,并将结果输出到页面上。

示例2.3 调用parseInt和parseFloat将字符串转换为数字

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Convert to Number</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
//<![CDATA[

function convertToNumber() {
   var sNum = "1.23e-2";
   document.writeln("<p>" + parseFloat(sNum) + "</p>");
   document.writeln("<p>" + parseInt(sNum) + "</p>");

   var fValue = parseFloat("1.45 inch");
   document.writeln("<p>" + fValue + "</p>");

   var iValue = parseInt("-33.50");
   document.writeln("<p>" + iValue + "</p>");

}
//]]>
</script>
</head>
<body onload="convertToNumber()">
 <p></p>
</body>
</html>

上述代码在Firefox、Safari/WebKit、Opera以及IE 8.0中的运行结果如下所示:

0.0123
1
1.45
-33

请注意,在输出的结果中第一个数值是以十进制形式显示的,而不是原字符串中所采用的科学计数法。第二个数值是调用parseInt函数转换得到的数字,因此在应用指数方式之前先截去了整数后面的部分。同样,在第四个转换中parseInt截去了数字中的小数部分。

对第三个数字的转换过程是非常有趣的。parseFloat函数会先从字符串“1.45 inch”中获取数字部分,直到遇到第一个特定于非数字的值为止。在该示例中,也就是“1.45”与“inch”之间的空格。接着将得到的字符串“1.45”转换成浮点数,如输出结果所示。

提示

 

示例2.3中的数字还需要转换回字符串,才能输出到页面中。

通过parseInt函数可以在十进制与八进制或十六进制之间进行来回转换。该函数的第二个参数是数字的进制,默认值为10。如果该参数值是2~36之间的数字,那么在生成字符串之前会先进行进制转换。

var iValue = parseInt("266",16);
document.writeln("<p>" + iValue + "</p>");

var iValue = parseInt("55",8);
document.writeln("<p>" + iValue + "</p>");

上述JavaScript代码的执行结果如下所示:

614[7]
45

除了parseInt和parseFloat函数之外,Number函数也可以用来将其他数据类型转换为数字。转换后的返回类型取决于数字的具体内容:包含浮点数的字符串将返回浮点数,而包含整数的字符串将返回整数。表2.6是其他数据类型与数字之间的转换表。

表2.6 其他数据类型与数字之间的转换表

输入

结果

undefined

NaN

null

0(在IE中将返回NaN)

布尔值

如果是true,那么返回1;否则返回0(在IE中将返回NaN)

数字

数字值

字符串

整数或浮点数(取决于字符串内容)

对象

NaN

除了将字符串转换为数字外,还可以通过isFinite[8]函数来判断变量的数值是否为无穷大。如果数值是无穷大或者NaN,那么该函数将返回false;否则将返回true。

第4章还将介绍Number对象的其他函数,这些函数的作用也是对数字进行操作。接下来的一节将介绍JavaScript中两种特殊的基本类型:null和undefined变量。

在JavaScript代码中,除了字面量、简单数据类型、对象以外,还应该了解两个变量,它们分别是表示不存在的null变量和未定义的undefined变量。

null变量是已定义的、值为null的变量。以下是null变量的一个示例:

var nullString = null;

如果变量已经声明但是还没有初始化,那么就是undefined变量:

var undefString;

如果声明了变量并且赋予了初始值,那么该变量就不是null或undefined:

var sValue = "";

当使用JavaScript库时,或者在一些复杂的代码中,某些变量有可能还没有初始化;如果尝试在表达式中使用这样的变量,那么就有可能得到出乎意料的结果,通常会导致JavaScript错误。如果不确定变量的状态,那么可以在条件表达式中测试该变量,例如:

if (sValue) ...  // 如果变量是null或undefined,那么结果为true;否则是false

第3章将详细介绍条件语句,但是现在你只需要知道该表达式会判断变量sValue是否已经声明并初始化,如果已声明并初始化则该表达式的值为true;否则,该表达式的值为false。

if (unknownVariable)  // false,变量没有声明或赋值
if (undefString)     // false,变量没有赋值
if (nullString)     // false,变量已经声明并且赋值,但是所赋的值是null,因此结果是false
if (sValue)       // true,变量已经声明并且赋值(包括空字符串)

使用null关键字,可以判断数值是否为null:

if (sValue == null)

在JavaScript中,即使变量已经声明,但只要还没有初始化就是undefined变量。如果变量已经声明并初始化,那么变量就不是null或undefined。然而,在该示例中它是一个全局变量,如前所述,没有以var关键字专门声明的变量可能会引起各种各样的问题。

提示

 

在本书中的某些代码段中,以//开始的注释可能会由于页面宽度的原因移到下一行,而不是因为它本身就是多行注释。

尽管不一定存在,不过在此还是要介绍另一个与变量类型相关的数值:NaN。如果一个字符串或布尔值变量不能转换为数字,那么所返回的数值将是NaN:

var nValue = 1.0;
if (nValue == 'one' )   // false,第二个操作数是NaN

isNaN函数可以用来检查变量值是否为NaN:

if (isNaN(sValue))    //如果字符串不能隐式转换为数字,就返回true

null值也是NaN。

提示

 

在O’Reilly 2006 ETech会议上,著名的技术专家和撰稿人Simon Willison发表了题为“A (Re)-Introduction to JavaScript”的演讲。在他的网站上可以找到这份演讲稿(http://simon.incutio.com/slides/ 2006/etech/javascript/js-tutorial.001.html)。

该演讲稿非常值得一读,以其中一句为例:“换言之,0、null、NaN以及空字符串都是false,而其他则都是true。”

在大多数情况下,JavaScript开发人员在编写代码的时候都会在声明变量的同时就进行初始化。所以,这些时候就无须显式地判断变量是否已经初始化。

不过,当使用复杂的大型JavaScript库或者具体的Web服务应答的应用程序时,判断变量是否初始化就将是非常重要的事,因为变量的声明和初始化并不在开发人员自己的控制范围之内。在访问应用程序的时候,了解null和undefined变量的行为也是非常重要的。

在有些情况下,我们希望为某个数值定义一个名字,然后通过这个名字以只读方式访问它。这个时候,可以使用const关键字来创建JavaScript常量:

const CURRENT_MONTH = 3.5;

常量可以是任意值,因为常量不可以赋值和重新赋值,所以在声明常量的时候就必须将常量初始化为一个固定的值。

和变量一样,JavaScript常量也可以是全局常量或局部常量。常量一般声明为全局常量,因为通常希望能够在所有JavaScript程序块中访问(并且不改变)该常量。同样,请注意整个常量名是全部大写的,这并不是强制要求的,但这是一种标准的命名约定,从而在代码中能够更加容易地识别出常量。

1.在下列变量名中,哪些是合法的变量名?哪些是非法的变量名?为什么?

$someVariable
_someVariable
1Variable
some_variable
somëvariable
function
some*variable

2.将下列标识符转成骆驼拼写法:

var some_month;
function theMonth    // 返回当前月份的函数
current-month       // 常量
var summer_month;    // 夏季月份的数组
MyLibrary-afunction  // JavaScript包中的某个函数

3.下列字符串是否合法?如果不合法,如何修改?

var someString = 'Who once said, "Only two things are infinite, the universe and human stupidity, and I'm not sure about the former."'

4.对于给定的数字432.54,用什么JavaScript函数可以返回该数字的整数部分?如何转换成八进制和十进制?

5.在JavaScript程序库中创建一个新的JavaScript函数,该函数包含一个名为someMonth的参数,如何判断该参数是否为null或者undefined变量?

1.下列变量名是合法的:

$someVariable
_someVariable
some_variable
somëvariable

下列变量名是非法的:

1Variable,因为首字母是非法的字母
function,因为它是保留字
some*variable,因为包含了非法字符*

2.转换后的标识符如下所示:

var someMonth
function getCurrentMonth
CURRENT_MONTH
summerMonths
myLibraryFunction

3.该字符串是非法的。修改方法是在双引号中只使用单引号,或者转义双引号:

var someString = "Who once said, 'Only two things are infinite, the universe and human stupidity, and I'm not sure about the former'";

或者
var someString = 'Who once said, "Only two things are infinite, the universe and human s tupidity, and I\'m not sure about the former"';

4.可以采用以下代码:

var fltNumber = 432.54;
var intNumber = parseInt(fltNumber);
var octNumber = intNumber.toString(8);
var hexNumber = intNumber.toString(16);

5.原书有误,已经根据勘误表。—译者注

勘误表的引文如下:

Use the following to test whether the variable has been assigned a value (not null and not undefined): if (a) { ... } If the value has never been declared, though, this test results in an error, as does passing the non-existent variable to the function. The way to prevent the error is to test the value before you pass it to the function, using the following: if (typof(a) != "undefined") { ... } Combined with the test for null above, you can test that a variable exists, is defined, and actually has a non-null value with: if ((typeof(a) != "undefined") && a) { ... }

使用以下代码测试变量是否赋值(非空并且已经定义):

if (a) {
  ...
}
然而,如果从来没有声明该值,该测试会导致一个错误,因为确实向该函数传递了不存在的变量。避免这种错误的方式而应在把该值传递给函数之前测试该值,使用下面的代码:
if (typof(a) != "undefined") { ... }

结合测试null的代码,可以测试一个变量是否存在,是否已经定义,是否实际上有一个非空null值:
if ((typeof(a) != "undefined") && a) { ... }

[1] 原书有误,已经根据O’Reilly网站的勘误表修改过。——译者注

[2] 原书有误,根据勘误表增加了3个关键字。——译者注

[3] 根据勘误表,删除横杠和下面的第2行代码。——译者注

[4] 根据勘误表修改为字母。——译者注

[5] 原书有误,根据勘误表修改过。——译者注

[6]  原书有误,根据勘误表修改过。——译者注

[7] 原书有误,根据勘误表修改过。——译者注

[8] 原书有误,根据勘误表修改过。——译者注

[9] 原书有误,根据勘误表修改过。——译者注

[10] const作为JavaScript关键字定义,但是在目前的IE中尚不支持。


相关图书

深入浅出Spring Boot 3.x
深入浅出Spring Boot 3.x
JavaScript核心原理:规范、逻辑与设计
JavaScript核心原理:规范、逻辑与设计
JavaScript入门经典(第7版)
JavaScript入门经典(第7版)
JavaScript函数式编程指南
JavaScript函数式编程指南
PHP、MySQL和JavaScript入门经典(第6版)
PHP、MySQL和JavaScript入门经典(第6版)
JavaScript学习指南(第3版)
JavaScript学习指南(第3版)

相关文章

相关课程