JavaScript核心概念及实践(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-11 23:38:13

点击下载

作者:邱俊涛 著

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

JavaScript核心概念及实践

JavaScript核心概念及实践试读:

前言

大概很少有程序设计语言可以担当得起“优美”这两个字,我们可以评价一种语言语法简洁、代码可读性强(尽管这一点主要依赖于开发人员的水平,而非语言本身)、富有表达力等,但是几乎不会说哪种语言是优美的,而JavaScript则是一个例外。

程序设计语言,主要可以分为两种。一种是我们平时接触较多的,工业级的程序设计语言,如 C/C++、Java、Object Pascal(DELPHI)等,从本质上来讲,这些语言是基于程序存储原理,即冯·诺依曼体系的,一般叫做命令式编程语言。而另一种,是根据阿隆左·丘奇的lambda演算而产生的,如Lisp、Scheme,被称为函数式编程语言。这两个体系一般情况下是互不干涉,泾渭分明的。这一现象直到JavaScript的逐渐成熟之后才被打破。函数式语言被认为是晦涩难懂的、学院派的,使用Lisp的似乎都是些披头散发、满口之乎者也而且性情古怪的大学教授。Emacs,这个被它的爱好者誉为世界上最强大、最好用的编辑器的插件机制,就是基于一个Lisp的方言完成的。Emacs算是函数式语言比较成功的运用案例之一。后来又出现了GIMP,一个Linux平台下的图形图像处理软件,它使用另一个Lisp的方言来进行自己的扩展。看来,函数式编程似乎已经被人们所接受了,然而事实并非如此简单,那种“前缀的操作符”,“函数为一等公民”的理念在短时间内是无法被大多数程序员所接受的,毕竟“数据结构+算法 =程序”之类箴言已经非常深入人心,而且被证明是一种行之有效的编程范式—直到JavaScript的出现。

JavaScript被称为具有C的语法的Lisp,它完美地结合了这两个体系。C的语法使得它迅速被习惯命令式编程的程序员所接受,从而得到了推广,而Lisp的内核则使得其代码以优美的形式和内涵超过了其他的命令式语言,成为非常流行的一门语言。根据TIOBE的编程语言排行统计,JavaScript一直排在前十位(在第8与第9之间徘徊)。然而,要转变长时间形成的编程习惯绝非易事,两个体系之间的一些理念具有根本性的差异,解决这个问题正是本书的一个目的,通过深入学习JavaScript的内核思想,我们可以将另一个体系的思想应用在日常的工作中,提高代码的质量。

而正是由于它与“主流”编程语言的“相似性”以及其强大的表达力,开发人员很容易写出可以工作的代码,但是诸如函数、闭包、作用域、原型链等机制在JavaScript都与其他语言大相径庭,因此本书希望通过详细讨论这些概念,帮助读者对JavaScript这些核心概念的理解。

JavaScript 并不像它表现出来的那样简单,大多数 JavaScript 程序员在无需深入理解JavaScript运行机制的情况下也可以写出可运行的代码,但是这样的代码几乎没有可维护性,当出现了一个隐藏的较深的 bug 的情况下,程序员往往无法很快定位错误可能的源头,从而花费大量的时间来进行alert调试。因此,理解JavaScript运行机制,以及澄清其容易被误解的特性将有助于杜绝这种现象。

另一方面,JavaScript作为一门脚本语言在早期并没有得到足够的重视,而且由于最初的JavaScript仅仅运行在浏览器端,可以访问的资源非常有限。但是随着Web的快速发展,开发人员开始将JavaScript运行在其他平台,并引入丰富的库做支持,这使得JavaScript成为了一个“全能”的编程语言。因此书中花费了很大的篇幅来讨论JavaScript在其他环境中的使用(如在Java应用中使用JavaScript完成脚本化,在C应用中使用SpiderMonkey等)。随着JavaScript代码的规模的增加,模块化及代码质量等问题也渐渐浮现,因此书中特别关注使用 JavaScript框架(backbone.js)来管理规模,用测试框架(QUnit、Jasmine)完成自动化测试等。

事实上, JavaScript 是一种非常依赖于框架的编程语言,框架在很大程度上影响了JavaScript开发人员的编程习惯和编程风格,比如使用jQuery框架的开发人员更关注函数式编程,而Backbone.js的使用者可能更着重在MVC及面向对象编程。

本书组织结构

本书可以分为两部分。第一部分为JavaScript语言的核心概念,包括JavaScript的对象、函数、数组、闭包,其中各个主题中会详细涉及到很多相关的、容易被误解的知识点,比如对象中的属性概念、函数中的匿名函数、作用域链、上下文、运行环境、JavaScript数组与其他语言数组的区别以及其强大之处等。

第一部分包括第1章到第7章,是随后内容的基础。读者需要尽可能地仔细阅读,并完全掌握,那样对随后的内容可以更好地理解。第一部分不包含JavaScript的基本语法,如流控制语句、变量的声明等内容,这些部分比较基础,和其他的编程语言相差不大,如果需要可以参阅其他书籍。同时,在本部分的例子中会穿插一些基本的语法知识,如果你曾经学过 C语言或者其他任何程序设计语言,都不会有阅读障碍。

第二部分包括第8章到第16章,着重讨论了JavaScript支持的编程范式,核心概念的深入,JavaScript的前端开发框架,测试框架,JavaScript引擎,JavaScript在Java、C、C++等应用中的使用,以及服务器端的JavaScript应用。这部分内容更注重实例,因为在掌握了JavaScript核心概念之后,我们需要将这些理解应用到实际工作中,以简洁优美的代码开发功能强大、且更容易维护、更容易扩展的应用程序。

各章内容简要说明如下。

第1章,介绍JavaScript的历史,语言特性及应用范围,从综合的视角来概述JavaScript。

第2章,介绍基本的JavaScript概念,这部分的概念十分重要,直接影响到后面章节的内容的理解。

第3章,对象是JavaScript中最核心、也最容易被误解的部分,所以抽出一个章节来描述 JavaScript的对象,涉及 JSON(JavaScript Object Notation),以及一些如何使用JavaScript对象的实例。

第4章,函数是JavaScript中的另一个重要的概念,与大多数为人熟知的命令式语言中的函数(方法)概念不一样的是,JavaScript 中的函数涉及到更复杂的形式,比如匿名函数、闭包等。

第5章,数组Array在JavaScript中是一个保留字,与其他语言不同的是,Array更像是一个哈希表,而对Array的操作则可以类比为栈结构,或者Lisp中的List,总之,数组是一个复杂的对象,值得我们花时间深入探究。

第6章,正则表达式是一个伟大的发明,在很多的应用程序和程序设计语言中都会出现它的身影,我们当然需要讨论其在 JavaScript 中的使用。其中包括正则表达式的规则及一些简单的实例。

第7章,闭包是函数式编程语言所特有的一种结构,使用它可以使代码更简洁,有时更是不可或缺,但是,粗心的设计或者编码很容易造成内存泄漏(特别是在 IE 浏览器的早期版本中)。

第8章,JavaScript作为一种语言,它本身又是“可编程”(programmable)的,你可以使用你自己设想的任意方式来组建你的代码,当然包括流行的 OO。本章的最后包含一个事件分发器的实现,通过这个例子我们可以较好地掌握面向对象的JavaScript。

第9章,探讨JavaScript中的函数式编程的主题,如果有Lisp或者Scheme之类的语言经验,可以从某种程度上获得共鸣。如果不了解其他的函数式语言,则应该仔细读这一章,对你的编程思想大有裨益。

第10章,在前面的章节中,陆续而分散地讨论过JavaScript语言中的一些核心概念如对象、函数、作用域、闭包等,但是不够深入,这一章则进行更详细地讨论。

第11章,这一章主要讨论客户端JavaScript的一些基础知识,以及一些好的编程实践。这些基础知识对第12章的阅读非常有帮助。

第12章,详细讨论一个流行的前端MVC框架backbone.js。使用backbone.js可以使你开发出模块化良好、层次清晰的前端应用。本章的最后给出了两个具体的实例,即纯前端的backgone应用程序和一个与后台服务集成的应用程序。

第13章,随着JavaScript代码规模的增加,人们对JavaScript代码的自动化测试的需求也更加迫切,这一章详细讨论QUnit.js和Jasmine两个测试框架的使用。

第14章,讨论JavaScript引擎,主要包括目前较为流行的三种引擎:Mozilla的Spider-monkey,Google的V8,以及前Sun的Rhino。其中剖析SpiderMonkey的工作机制, V8及Rhino仅演示一些基本的使用。

第15章,讨论JavaScript在Java应用程序中的使用,实例部分介绍笔者开发的待办事项管理工具sTodo及可编程计算器phoc的设计和实现,以及其中如何使用JavaScript引擎来完成脚本化。

第16章,讨论JavaScript在服务器端的应用,分别讨论了node.js、MongoDB和另一个面向文档的数据库系统CouchDB的基本使用。

附录A,讨论了一些常用JavaScript技巧。

附录B,讨论了前端JavaScript框架jQuery及一个实例。

本书主要面向想要学习JavaScript的开发人员,或者JavaScript的初学者,或者那些原本认为 JavaScript 仅能做一些琐碎或者无关紧要工作的开发者。如果你之前有其他编程语言如Java、C等的编程经验,那么在看到一些关于语言之间类比的示例时可能会得到更好的效果,然而这些并非必须,作者在使用其他语言做示例时,尽可能地使用了语言本身的特性,而且这种内容占用的篇幅都非常小。本书的第14章,需要C/C++语言的编程知识,而第15章,则需要对Java语言有一定的理解。

应该注意的是,本书不是一本JavaScript的入门书籍,关于表达式、条件、分支等编程的基本概念都没有介绍(虽然它们并不复杂),因此如果你之前完全没有接触过编程,请先阅读其他书籍。

如何使用本书

本书中前半部分中讲解的大部分内容与客户端的JavaScript没有关系,如函数、对象、数组、闭包等概念,都属于JavaScript内核本身,是与环境无关的。为了避免过早地进入具体的应用中,笔者开发了一个简单但可用的JavaScript执行环境(JSEvaluator),核心采用Mozilla的一个开源的JavaScript引擎Rhino,这个引擎为纯Java实现,不包含任何DOM元素,故可以较为轻便地运行书中的例子而不必纠缠与浏览器差异之类的问题中。

JSEvaluator(github)是一个简单的JavaScript的IDE,提供基本的代码编辑功能,单击运行按钮可以运行当前活动标签中的脚本,结果将在JSEvaluator的控制台中打印出来如下图所示。本书的后半部分,如第7章的事件分发器以及第9章的客户端JavaScript,则需要在浏览器中运行。具体的章节会有详细说明。JSevaluator界面

程序设计是一门实践的艺术,读者在阅读本书的同时,应该做一些练习,那样才可能对书本中的知识点有更好的理解。建议读者一边阅读,一边将书中的例子在JSEvaluator中运行,查看结果,并可以自己修改这些例子,以期得到更好的效果。

致谢

正如所有技术类书籍的前言部分所描述的那样,几乎没有任何一位作者宣称自己独自完成了某一部著作。在进行广泛而深入地研究技术本身时,我们必然会在别人研究的基础上展开工作,才能更好、更高效地进入该领域。

本书在撰写过程中,参考了众多的资料、文献以及相关的标准规范等,当然也和很多的朋友进行过讨论,这些朋友有现实世界中的同事,也有在虚拟网络中素未谋面的同好。本书的部分章节,最早发表在iteye上。发表之初,得到了很多朋友的反馈,我在发布一个里程碑版本(0.9版)的时候,详细整理了他们的意见,并根据这些意见做了修改,这里也要一并感谢。

本书最初是发布在网络上的(Javaeye 论坛)的系列文章(《JavaScript 内核》)。陆续整理出来之后,读者朋友非常积极地就一些问题与我讨论,在时间充裕的情况下,我都做过解答。后来由于工作上和其他方面的一些原因,很难有闲暇时间做进一步的整理,因此 2010 年后半年的很多问题都没有及时回答,这点向读者朋友们道歉,也请大家谅解。在此,感谢Javaeye论坛上那些积极给予反馈的读者朋友,没有你们的支持和关注,本书不可能最终出版。

本书规划于2009年12月,2010年1月开始动笔,期间经历了很多生活上的杂事,感谢我的妻子孙女士在此期间对我的支持。编辑书本是一件充满挑战而有时又会充满枯燥的过程,她经常鼓励我继续写下去,没有她,此书无法与诸位读者见面。在本书动笔之前的研究期间,笔者还得到前公司的胡东先生的谆谆教诲和启发。胡东先生是一位沉湎于自己精心构筑的技术世界而不能自拔的老师,在此一并感谢。

感谢ThoughtWorks的胡凯、张凯峰,没有他们的支持和帮助,本书肯定无法变成纸质出版物。感谢其他ThoughtWorks的同事,他们时时传递着正能量,给了我很多的鼓励。感谢本书的编辑陈冀康,根据他的建议,我对内容做了很多大的修正,使得内容更加充实,也更加易懂。邱俊涛2011年1月25日于昆明

作者简介

邱俊涛,软件工程师,现就职于 ThoughtWorks,喜欢编程,尤其喜欢编程带来的成就感。对动态语言、函数式编程等有浓厚的兴趣。喜欢开源软件,喜欢知识分享,并从与他人的分享中学习。崇尚简单、轻量的设计和模式。个人主页:http://icodeit.org。微博@正反反长。第1章概述1.1 JavaScript简史

20世纪90年代,在早期的Web站点上,所有的网页内容都是静态的。所谓静态是指除了点击超链接外,你无法通过任何方式同页面进行交互,比如让页面元素接受事件,修改字体等。但是人们又迫切地需要一种方式来打破这个局限,于是到了1996年,网景(Netscape)公司开始研发一种新的语言Mocha,并将其嵌入到自己的浏览器Netscape中。这种语言可以通过操纵DOM(Document Object Model,文档对象模型)来修改页面,并加入了对鼠标事件的支持。Mocha 使用了 C 的语法,但是设计思想上主要从函数式语言 Scheme 那里获得了灵感。当Netscape 2发布的时候,Mocha被改名为LiveScript,当时可能是想让LiveScript为Web页面注入更多的活力。后来,考虑到这个脚本语言的推广,网景采取了一种宣传策略,将 LiveScript更名为JavaScript,目的是为了跟当时非常流行的面向对象语言Java发生暧昧的关系。这种策略显然颇具成效,以至于到现在很多初学者还会为JavaScript和Java的关系而感到困惑。

JavaScript取得成功了之后,确实为页面注入了活力,微软也紧接着开发自己的浏览器脚本语言,一个是基于BASIC语言的VBScript,另一个是跟JavaScript非常类似的Jscript。但是由于JavaScript已经深入人心,所以在随后的版本中,微软的IE几乎是将JavaScript作为一个标准来实现。当然,两者仍然有不兼容的地方。1996年后期,网景向欧洲计算机厂商协会(ECMA)提交了JavaScript的设计,以申请标准化,ECMA去掉了其中的一些实现,提出了ECMA-262标准,并确定JavaScript的正式名字为ECMAScript,但是JavaScript的名字已经深入人心,故本书中仍沿用JavaScript这个名字。1.1.1 动态网页

Web 页面在刚开始的时候,是不能动态修改其内容的。要改变一个页面的内容,需要先对网站上的静态HTML文件进行修改,然后需要刷新浏览器。后来出现的JSP、ASP等服务器端语言可以为页面提供动态的内容,但是如果没有JavaScript则无法在服务器返回之后动态地在前端修改页面,也无法有诸如鼠标移上某页面元素则高亮该元素之类的效果,因此JavaScript的出现大大丰富了页面的表现,提高了用户体验。

而当Ajax流行起来之后,更多的非常绚丽的Web应用涌现了,而且呈越来越多的趋势,如Gmail,Google Map,Google Reader,Remember the milk,Facebook等优秀的Web 2.0应用,都大量使用了JavaScript以及基于JavaScript技术的Ajax。

这些优秀的Web 2.0应用提供动态的内容,客户端可以局部更新页面上的视觉元素,比如对地图的放大/缩小,新邮件到来后的提醒等。用户体验较静态页面得到了很大的提升。事实上,后期的很多应用均建立在B/S架构上,因为HTML构筑UI的成本较桌面开发低。因此基于Web的应用开始占有一定的份额,正在逐步替换C/S架构的桌面应用。

动态网页的好处在于,客户端的负载较小,只需要一个浏览器即可,主要的负担在服务器端,这就节约了客户端的开发成本。但是前端页面正在向着复杂化的方面发展,随着前端的MVC框架的发展(backbone.js,javascriptMVC等),JavaScript正在变得更加复杂,甚至在一些互联网应用中,前端代码的代码量已经和后端的代码量相当。一个典型的模型是:后台服务提供RESTFul形式的API,以提供对资源的所有操作,而前端则完全独立为一个应用程序,仅把后台程序作为数据源。1.1.2 浏览器之战

1994年网景公司成立,并推出了自己的浏览器的免费版本Netscape,很快就占有了浏览器市场。到了 1995年,微软公司开始加入,并很快发布了自己的 Internet Explorer 1.0。在随后的几年间,网景和微软公司不停地发布新版本的浏览器,支持更多的新功能。很快,这两者的目标就不是如何做好浏览器,而是在对手擅长的方面压制对方。比如,网景的浏览器Netscape标榜速度快,IE就要开发出比网景更快的浏览器,而对自身的安全漏洞,渲染能力等方面放任自流。这样纯粹为了竞争而竞争,对广大的用户来说无疑是非常不利的事情。但是一直到1997年,网景的浏览器Netscape份额大概在72%,而IE只占到18%。

但是,IE在随后的版本IE4.0的时候开始支持W3C的标准,并且在网页的动态性方面加入了很大的支持。事实上,这时候的网景已经不敌慢慢崛起的微软帝国了,微软利用自己的操作系统Windows,在其中捆绑了IE浏览器,而且完全免费。这样,IE的市场占有率开始超过Netscape。当出现一家独大的场面之后,标准化就显得步履维艰了,开发人员开始只为IE浏览器编写代码,因为不需要在其他任何浏览器上运行,所有的网页都很可能只能在IE下运行,或者只能在IE下效果才可以得到保证。

1998年,网景的Netscape开放了源码,分散在世界各地的开发人员开始贡献代码和补丁,使得这个浏览器变得越来越出色。到了2004年,Firefox,作为这个项目中的一个产品,推出了 1.0版本。这个以Mozilla为基础的浏览器才慢慢开始发展。一方面,捆绑在Windows XP系统中的IE6.0漏洞百出,大量的蠕虫病毒都会攻击IE浏览器,而Firefox则没有这方面的问题,安全且高效。因此从2006年到2008年,Firefox的市场占有率开始回升,IE的平均占有率大约为85%,Firefox平均占有率为15%。而某些地区,如在欧洲,Firefox的占有率高达20%。

到了 2009年,由于反垄断法及开源项目的影响,Windows 7不再捆绑 IE浏览器,这样,用户可以有权利选择自己需要的浏览器,但这并不意味着Firefox胜出,IE落败。事实上,这更促进了其他的浏览器如Safari、Opera、Chrome的发展。

一些“小众”的浏览器正在变得越来越流行,并占据了相当的市场份额,比如Firefox和Chrome,在很多场景下Chrome甚至已经超过了IE的份额。1.2 JavaScript语言特性

JavaScript是一门动态的、弱类型、基于原型的脚本语言。在JavaScript中“一切皆对象”,在这一方面,它比其他的面向对象的语言来得更为彻底。即使作为代码本身载体的函数(function),也是对象,数据与代码的界限在 JavaScript 中已经相当模糊。虽然它被广泛应用在Web客户端,但是其应用范围远远未局限于此。下面就这几个特点分别介绍。1.2.1 动态性

动态性是指,在一个JavaScript对象中,要为一个属性赋值,我们不必事先创建一个字段,只需要在使用的时候做赋值操作即可。如下例:

//定义一个对象

var obj = new Object();

//动态创建属性name

obj.name = "an object";

//动态创建属性sayHi

obj.sayHi = function(){

 return "Hi";

}

obj.sayHi();

当不需要一个对象的属性时,我们可以很轻易地将其从对象上删除。

obj.name

"juntao"

delete obj.name

obj.name

undefined

假如我们使用Java语言,代码可能会是这样:

class Obj{

  String name;

  Function sayHi;

  public Obj(Sting name, Function sayHi){

  this.name = name;

  this.sayHi = sayHi;

 }

}

Obj obj = new Obj("an object", new Function());

可以看出,在静态语言中,需要预先定义好对象需要什么属性,属性自身的类型是什么,当定义完成之后,对象的结构就定下来了,之后都保持这种结构而无法更改。通常对象可能会继承一些自己用不到的方法,而且也无法删除。

另一个有趣的例子是动态地访问一个JavaScript对象的属性。

var key = "property";

print(key);

"property"

var obj = {

  property: "my property"

}

print(obj[key]);

"my property"

应该注意到 key 是一个变量,其值为“property”,当执行 obj[key]时,key 被求值为“property”,然后去除 obj["property"],这个特性可以使得代码更加简洁清晰,比如可以动态地从代码中生成对象的属性名,然后去除属性值等。1.2.2 弱类型

与Java、C/C++不同,JavaScript是弱类型的,它的数据类型无需在声明时指定,解释器会根据上下文对变量进行实例化,比如:

//定义一个变量s,并赋值为字符串

var s = "text";

print(s);

//赋值s为整型

s = 12+5;

print(s);

//赋值s为浮点型

s = 6.3;

print(s);

//赋值s为一个对象

s = new Object();

s.name = "object";

print(s.name);

结果为:

text

17

6.3

Object

在JavaScript中,类型是和值关联的,而不是和变量关联。弱类型具有很大的灵活性,在定义变量时无须显式声明。不过,弱类型也有其不利的一面,比如在开发面向对象的JavaScript的时候,没有类型的判断将会是比较麻烦的问题,不过我们可以通过别的途径来解决此问题。1.2.3 面向对象

虽然与主流的面向对象语言中的面向对象的概念大相径庭,但是在JavaScript中,一切都是对象!只不过 JavaScript 中,这一点更为彻底一些,甚至用以表达逻辑的函数/代码本身也是对象,比如代码本身可以作为参数传递给其他的代码。

var array = [1, 2, 3, 4, 5];

array.map(function(item){

 return item * 2;

});

运行结果如下。

[2, 4, 6, 8, 10]

数组array中有5个元素,每个元素是一个数字。数组的map方法会接受一个匿名函数,这样对于数组中的每个元素,都会调用这个匿名函数(返回元素乘以 2 的值),最后把结果放入结果数组。有意思的是此处的 map,它可以接受函数作为参数。map 函数可以处理更为复杂的场景,比如数组中的每个元素都是一个复杂对象。

var staff = [

  {name: 'abruzzi', age: 24},

  {name: 'bajmine', age: 26},

  {name: 'chris', age: 25}

];

staff.map(function(item){

 return item.name.toUpperCase();

});

运行结果如下。

['ABRUZZI', 'BAJMINE', 'CHRIS']

在上例中,map用以将数组staff中的每个元素的name属性取出,转换为大写字母,并生成新的数组,而将对于那些不关心的其他属性(比如此处的age)排除在结果集之外。

另一个例子是与 map 很类似的函数 filter,用于过滤数组中满足某些条件的元素,filter的使用方法与map一样,也接受一个函数。

staff.filter(function(item){

 return item.age > 24;

});

这样结果中仅包含age大于24的条目。

[

 {name: 'bajmine', age: 26},

 {name: 'chris', age: 25}

]

这两个例子中,可以看到函数可以像其他任何数据类型(字符串、数字)那样,被轻易地传递给其他函数。在JavaScript中一切都是对象。1.2.4 解释与编译

通常来说,JavaScript 是一门解释型的语言,特别是在浏览器中的 JavaScript,所有的主流浏览器都将JavaScript作为一个解释型的脚本来进行解析。然而,这并非定则,在Java版的JavaScript解释器Rhino中,脚本可以被编译为Java字节码。Google的V8引擎则直接将JavaScript代码编译为本地代码,无需解释。

解释型的语言有一定的好处,即可以随时修改代码,无需编译,刷新页面即可重新解释,可以实时看到程序的结果,但是由于每一次都需要解释,程序的开销较大;而编译型的语言则仅需要编译一次,每次都运行编译过的代码即可,但是又丧失了动态性。

我们将在第9章和第10章对两种方式进行更深入的讨论。1.3 JavaScript应用范围

当JavaScript第一次出现的时候,是为了给页面带来更多的动态,使用户可以与页面进行交互。虽然JavaScript在Web客户端取得了很大的成功,但是ECMA标准并没有局限其应用范围。事实上,现在的JavaScript大多运行于客户端,但是仍有部分运行于服务器端,如Servlet、ASP等。当然,JavaScript作为一个独立的语言,同样可以运行在其他的应用程序中,比如Java版的JavaScript引擎Rhino、C语言版的SpiderMonkey等,使用这些引擎,可以将JavaScript应用在任何应用之中。1.3.1 客户端JavaScript

客户端的JavaScript随着Ajax技术的复兴,越来越凸显了JavaScript的特点,也有越来越多的开发人员开始进行JavaScript的学习。使用JavaScript,你可以使你的Web页面更加生动,通过 Ajax,无刷新地更新页面内容,可以大大提高用户体验。随着大量的 JavaScript 库和框架如jQuery、ExtJS、Backbone.js、Mootools等的涌现,越来越多的绚丽、高体验的Web应用被开发出来,这些都离不开幕后的JavaScript的支持,如图1-1所示。图1-1 JavaScript实现的一个Web幻灯片

浏览器中的 JavaScript引擎也有了长足的发展,比如 FireFox 3,当时一个宣传的重点就是速度比IE快。这个速度一方面体现在页面渲染上,另一方面则体现在JavaScript引擎上。而Google的Chrome的JavaScript引擎V8更是将速度发展到了极致。很难想象,如果没有JavaScript,如今的大量网站和Web应用会成为什么样子。

我们可以看几个例子,来说明客户端的 JavaScript 的应用程度,图 1-2 和图 1-3 使用了ExtJS,它是一个非常优秀的JavaScript库。图1-2 ExtJS实现的一个网络相册图1-3 ExtJS实现的一个表格,具有排序、编辑等功能

当然,客户端的JavaScript各有侧重,jQuery以功能见长,通过选择器以及对选择起的操作,可以快速高效地完成大部分前端开发工作,另一方面,jQuery 并且提供灵活、强大的插件机制,图1-4为jQuery的UI插件。图1-4 jQuery-UI的日期对话框

基于jQuery的插件非常丰富,开发者也可以很容易地开发出适应各种场景的插件供项目使用。

总之,随着Ajax的复兴,客户端的JavaScript得到了很大的发展,网络上流行着大量的优秀的JavaScript库,现在有一个感性的认识即可,我们在后边的章节会择其优者进行详细讨论。1.3.2 服务器端JavaScript

相对客户端而言,服务器端的JavaScript相对平淡很多,但是随着JavaScript被更多的开发人员重视,JavaScript在服务器端也开始迅速发展起来:Node.js,Helma,Apache Sling等。在服务器端的JavaScript比客户端少了许多限制,如对本地文件的访问,对网络、数据库资源的访问等。

一个比较有意思的服务器端JavaScript的例子是Aptana的Jaxer。Jaxer是一个服务器端的Ajax框架,我们可以看这样一个例子(例子来源于 jQuery的设计与实现者 John Resig)。

 

 

 

 

  

  

 

runat属性说明脚本运行在客户端还是服务器端,client表示运行在客户端,server表示运行在服务器端,而both表示可以运行在客户端和服务器端,这个脚本可以访问文件,并将文件加载到一个textarea的DOM元素中,还可以将textarea的内容通过Form表单提交给服务器并保存。

再来看另一个例子,通过Jaxer对数据库进行访问。

通过动态、灵活的语法,再加上对原生的资源(如数据库、文件、网络等)操作的支持,服务器端的JavaScript应用将会越来越广泛。

当Google的JavaScript引擎V8出现以后,有很多基于V8引擎的应用也出现了,其中最著名、最有前景的当算node.js了。下面我们来看一下node.js的例子。

var sys = require('sys'),

  http = require('http');

http.createServer(function (req, res) {

  setTimeout(function () {

   res.sendHeader(200, {'Content-Type': 'text/plain'});

   res.sendBody('Hello World');

  res.finish();

  }, 2000);

}).listen(8000);

sys.puts('Server running at http://127.0.0.1:8000/');

保存这个脚本为sayHello.js,然后运行:

node sayHello.js

程序将会在控制台上打印:

Server running at http://127.0.0.1:8000/

访问http://127.0.0.1:8000,两秒钟之后页面会响应:Hello, World。

再来看另一个官方提供的例子。

var tcp = require('tcp');

var server = tcp.createServer(function (socket) {

 socket.setEncoding("utf8");

  socket.addListener("connect", function () {

  socket.send("hello\r\n");

 });

  socket.addListener("receive", function (data) {

  socket.send(data);

 });

  socket.addListener("eof", function () {

  socket.send("goodbye\r\n");

  socket.close();

 });

});

server.listen(7000, "localhost");

访问localhost的7000端口,将建立一个TCP连接,编码方式为utf-8,当客户端连接到来时,程序在控制台上打印:

hello

当接收到新的数据时,会将接收到的数据原样返回给客户端,如果客户端断开连接,则向控制台打印:

goodbay

Node提供了丰富的API来简化服务器端的网络编程。由于Node是基于JavaScript语言,因此天生就具有很好的动态性和可扩展性,特别时JavaScript在函数式编程方面的一些模式,对于网络程序的异步模式非常匹配。因此在开发网络程序上,这确实是一个不错的选择。

Node.js运行实现了JavaScript的模块规范CommonJS,使得大规模的JavaScript开发成为可能,一个模块可以使用其他的任何模块,或者将自己需要公开的函数导出,提供给别的模块使用(本书的第16章讨论了Node.js的一些设计初衷,并提供了一个实例)。1.3.3 其他应用中的JavaScript

通过使用 JavaScript 的引擎的独立实现,比如 Rhino、SpliderMonkey、V8 等,可以将JavaScript应用到几乎所有的领域,比如应用程序的插件机制,高级的配置文件分析,用户可定制功能的应用,以及一些类似于浏览器场景的比如 Mozilla 的 ThunderBrid,Mozilla 的 UI框架XUL,笔者开发的一个Todo管理器sTodo(在第15章详细讨论)等,如图1-5所示。

Java版的JavaScript引擎原生地可以通过使用Java对象,大大提高JavaScript的应用范围,如数据库操作、服务器内部数据处理等。当然,JavaScript这种动态语言,在UI方面的应用最为广泛。

著名的Adobe Reader也支持 JavaScript扩展,并提供 JavaScript的API来访问PDF文档内容,可以通过 JavaScript来定制Adobe Reader的界面以及功能等。图1-5 sTodo一个使用 JavaScript来提供插件机制的 Java桌面应用

app.addMenuItem({

  cName: "-",

  // menu divider

  cParent: "View",

  // append to the View menu

  cExec: "void(0);"

});

app.addMenuItem({

  cName: "Bookmark This Page &5",

  cParent: "View",

  cExec: "AddBookmark();",

  cEnable: "event.rc= (event.target != null);"

});

app.addMenuItem({

  cName: "Go To Bookmark &6",

  cParent: "View",

  cExec: "ShowBookmarks();",

  cEnable: "event.rc= (event.target != null);"

});

app.addMenuItem({

  cName: "Remove a Bookmark",

  cParent: "View",

  cExec: "DropBookmark();",

  cEnable: "event.rc= (event.target != null);"

});

app.addMenuItem({

  cName: "Clear Bookmarks",

  cParent: "View",

  cExec: "ClearBookmarks();",

  cEnable: "event.rc= true;"

});

为Adobe Reader添加了4个菜单项,如图 1-6所示。图1-6 Adobe Reader中的 JavaScript书签插件

UltraEdit编辑器在新的版本中也加入了对脚本化的支持,使用的脚本语言正是JavaScript,用户可以通过脚本来控制编辑器的一些公开对象。下面是一个简单的实例,如图1-7所示。图1-7 UltraEdit编辑器

UltraEdit.activeDocument表示当前活动文档,write方法会将字符串写入当前活动文档的缓冲区,运行这个脚本,可以得到下面的效果,如图1-8所示。图1-8 UltraEdit中的 JavaScript脚本运行结果

当然,这里仅是一个简单实例,关于UltraEdit脚本化的详细信息请参考UltraEdit的帮助文件或相关文档。

另一个比较有意思的JavaScript实例为一个在线的遗传算法的演示。给定一个图片,然后将一些多边形(各种颜色)拼成一个图片,拼图的规则为使用遗传算法,使得这些多变形组成的图片与目标图片最为相似,如图1-9所示。图1-9 JavaScript在线遗传算法演示

可见,JavaScript在其他方面也得到了广泛的应用。第2章基本概念

本章将聚焦于JavaScript中的基本概念,这些概念与传统语言有比较大的不同,因此单独列出一章来做专门描述。理解本章的概念对书中后续章节的概念,代码的行为等会有很大的帮助,读者不妨花比较多的时间阅读本章,即使你对JavaScript已经比较熟悉,也建议通读本章。

本章主要讲述JavaScript中的数据类型(基本类型与引用类型)、变量(包括变量的作用域)、操作符(主要是一些较为常见,但是不容易从字面上理解的操作符)。由于 JavaScript中的“一切皆对象”,在掌握了这些基本的概念之后,读者就可以较为轻松地理解诸如作用域、调用对象、闭包等较难理解的概念了。2.1 数据类型

有程序设计经验的读者肯定知道,在C或者Java这样的语言中,数据是有类型的。比如用以表示用户名的属性是字符串,而一个雇员的年龄则是一个数字,表示UI上的一个开关按钮的数据模型则为布尔值等,对数字可能还可以细分为浮点数、整型数,整型数又可能分为长整型和短整型,总而言之,它们都表示语言中的数据的值的类型。

JavaScript中的数据类型分为两种:基本数据类型和对象类型。其中对象类型包含对象、数组以及函数,基本数据类型在必要时会被隐式地转换为对象。2.1.1 数据类型

在JavaScript中,包含6种数据类型、字符串(string)、数值(number)、布尔值(boolean), undefined、null及对象(object)。下面是一些简单的例子。

var str = "Hello, world";//字符串

var i = 10;//整型数

var f = 2.3;//浮点数

var b = true;//布尔值

我们可以分别查看变量的值及变量的类型:

print(str);

print(i);

print(f);

print(b);

print(typeof str);

print(typeof i);

print(typeof f);

print(typeof b);

print(typeof x);

注意,在此处使用的 print()函数为 Rhino 解释器的顶层对象的方法,可以用来打印字符串。通常情况下,在客户端,程序员多使用alert()进行类似的动作,alert()是浏览器中JavaScript解释器的顶层对象(window)的一个方法。

Hello, world

10

2.3

true

string

number

number

Boolean

undefined

在JavaScript中,所有的数字,不论是整型、浮点型,都属于“数字”基本类型。typeof是一个一元的操作符,在本章的另外一个小节会专门讲到。2.1.2 对象类型

这里提到的对象不是对象本身,而是指一种类型,我们在第 3 章会对对象进行详细的讨论,此处的对象包括:对象(属性的集合,即键值的散列表)、数组(有序的列表)、函数(包含可执行的代码)。

对象类型是一种复合的数据类型,其基本元素由基本数据类型组成,当然不限于基本类型,比如对象类型中的值可以是其他的对象类型实例,我们通过例子来说明。

var str = "Hello, world";

var obj = new Object();

obj.str = str;

obj.num = 2.3;

var array = new Array("foo", "bar", "zoo");

var func = function(){

  print("I am a function here");

}

可以看到,对象具有属性,如 obj.str、obj.num,这些属性的值可以是基本类型,事实上还可以更复杂,我们来看看它们的类型。

print(typeof obj);

print(typeof array);

print(typeof func);

//将打印出

object

object

function

读者可能会对 print(typeof array)打印出 object感到奇怪,事实上,对象和数组的界限并不那么明显(事实上它们是属于同一类型的),但是它们的行为却非常不同,本书的后续章节将两个重要的数据类型做了分别介绍。2.1.3 基本类型与对象间的转换

类似于Java中基本数据类型的自动装箱拆箱,JavaScript也有相应的动作,基本数据类型在做一些运算时,会临时包装一个对象,做完运算后,又自动释放该对象。我们可以通过几个例子来说明。

var str = "JavaScript Kernal";

print(str.length);//打印17

print(typeof str);//打印string

str为一个字符串,通过typeof可以得知其type为"string",而:

var str2 = new String("JavaScript Kernal");

print(typeof str2);//打印object

两次的typeof操作的结果来看,这两者并不相同,那么为什么可以使用str.length来得到str的长度呢?事实上,当使用str.length时,JavaScript会自动包装一个临时的String对象,内容为str的内容,然后获取该对象的length属性,最后,这个临时的对象将被释放。

而将对象转换为基本类型则是通过这样的方式:通过调用对象的valueOf()方法来取得对象的值,如果和上下文的类型匹配,则使用该值。如果valueOf取不到值的话,则需要调用对象的toString()方法,而如果上下文为数值型,则又需要将此字符串转换为数值。由于JavaScript是弱类型的,所以JavaScript引擎需要根据上下文来“猜测”对象的类型,这就使得JavaScript的效率比编译型的语言要差一些。valueOf()的优先级要高于toString()。

valueOf()的作用是,将一个对象的值转换成一种合乎上下文需求的基本类型,toString()则名副其实,可以打印出对象对应的字符串,当然前提是你已经“重载”了Object的toString()方法。

事实上,这种转换规则会导致很多的问题,比如,所有的非空对象,在布尔值环境下,都会被转成true,比如下面这个例子。

function convertTest(){

 if(new Boolean(false) && new Object() &&

  new String("") && new Array()){

    print("convert to boolean")

 }

}

convertTest();//convert to Boolean

初学者容易被 JavaScript 中的类型转换规则搞晕,很多情况下会觉得那种写法看着非常别扭,其实只需要掌握了规则,这些古怪的写法会大大提高代码的性能。我们通过例子来学习这些规则。

var x = 3;

var y = x + "2";// => 32

var z = x + 2;// => 5

print(y);

print(z);

通常可以在JavaScript代码中发现这样的代码:

if(datamodel.item){

  //do something...

}else{

  datamodel.item = new Item();

}

这种写法事实上具有更深层次的含义。

应该注意到,datamodel.item是一个对象(字符串、数字等),而if需要一个boolean型的表达式,所以这里进行了类型转换。在JavaScript中,如果上下文需要boolean型的值,则引擎会自动将对象转换为boolean类型。转换规则为:如果该对象非空,则转换为true,否则为false。因此我们可以采取这种简写的形式。

而在传统的编程语言(强类型)中,我们则需要:

if(datamodel.item != null){

  //do something...

}else{

  datamodel.item = new Item();

}2.1.4 类型的判断

前面讲到JavaScript特性的时候,我们说过,JavaScript是一个弱类型的语言,但是有时我们需要知道变量/参数在运行时的类型,比如,一个函数接受的参数类型为函数类型。

function handleMessage(message, handle){

 return handle(message);

}

当调用handleMessage的函数传递的handle不是一个函数则JavaScript引擎会报错,因此我们有必要在调用之前进行判断。

function handleMessage(message, handle){

 if(typeof handle == "function"){

  return handle(message);

 }else{

  throw new Error("the 2nd argument should be a function");

 }

}

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载