Archive for the ‘JavaScript’ Category

JavaScript 技巧

JavaScript 的成功让人津津乐道,为 Web 网页编写 JavaScript 代码已经是所有 Web 设计师的基本功,这门有趣的语言蕴藏着许多不为人熟知的东西,即使多年的 JavaScript 程序员,也未能完全吃透。本文从7个方面讲述 JavaScript 中那些你不很熟知但非常实用的技巧。

简略语句 
JavaScript 可以使用简略语句快速创建对象和数组,比如下面的代码:



可以使用简略语句如下:



对象 car 就此创建,不过需要特别注意,结束花括号前一定不要加 ";" 否则在 IE 会遇到很大麻烦。

创建数组的传统方法是:



使用简略语句则:



另一个可以使用简略语句的地方是条件判断语句:




可以简略为:


JSON 数据格式 
JSON 是 "JavaScript Object Notation" 的缩写,由 Douglas Crockford 设计,JSON 改变了 JavaScript 在缓存复杂数据格式方面的困境,如下例,假如你要描述一个乐队,可以这样写:



你可以在 JavaScript 中直接使用 JSON,甚至作为某些 API 的返回数据对象,以下代码调用著名书签网站 
delicious.com 的一个 API,返回你在该网站的所有书签,并显示在你自己的网站:


JavaScript 本地函数 (Math, Array 和 String) 
JavaScript 有很多内置函数,有效的使用,可以避免很多不必要的代码,比如,从一个数组中找出最大值,传统的方法是:



使用内置函数可以更容易实现:



另一个方法是使用 Math.max() 方法:



你可以用这个方法帮助探测浏览器



这解决了 IE 浏览器的一个问题,通过这种方法,你总是可以找到那个正确的值,因为浏览器不支持的那个值会返回 undefined。

还可以使用 JavaScript 内置的 split() 和 join() 函数处理 HTML 对象的 CSS 类名,如果 HTML 对象的类名是空格隔开的多个名字,你在为它追加或删除一个 CSS 类名的时候需要特别注意,如果该对象还没有类名属性,可以直接将新的类名赋予它,如果已经存在类名,新增的类名前必须有一个空格,用传统的 JavaScript 方法是这样实现的:



使用 split 和 join 方法则直观优雅得多:


事件代理 
与其在 HTML 文档中设计一堆事件,不如直接设计一个事件代理,举例说明,假如你有一些链接,用户点击后不想打开链接,而是执行某个事件,HTML 代码如下:



传统的事件处理是遍历各个链接,加上各自的事件处理:



使用事件代理,可以直接处理,无需遍历:


匿名函数与 Module 模式 
JavaScript 的一个问题是,任何变量,函数或是对象,除非是在某个函数内部定义,否则,就是全局的,意味着同一网页的别的代码可以访问并改写这个变量(
ECMA 的 JavaScript 5 已经改变了这一状况 – 译者),使用匿名函数,你可以绕过这一问题。

比如,你有这样一段代码,很显然,变量 name, age, status 将成为全局变量



为了避免这一问题,你可以使用匿名函数:



如果这个函数不会被调用,可以更直接为:



如果要访问其中的对象或函数,可以:



这就是所谓 Module 模式或单例模式(Singleton),该模式为 Douglas Crockford 所推崇,并被大量应用在 
Yahoo User Interface Library YUI

假如你想在别的地方调用里面的方法,又不想在调用前使用 myApplication 这个对象名,可以在匿名函数中返回这些方法,甚至用简称返回:


代码配置 
别人使用你编写的 JavaScript 代码的时候,难免会更改某些代码,但这会很困难,因为不是每个人都很容易读懂别人的代码,与其这样,不如创建一个代码配置对象,别人只需要在这个对象中更改某些配置即可实现代码的更改。这里有一篇 
JavaScript 配置对象详解的文章,简单说:

  • 在代码中创建一个叫做 configuration 的对象
  • 里面保存所有可以更改的配置,如 CSS ID 和类名,按钮的标签文字,描述性文字,本地化语言设置
  • 将该对象设置为全局对象,以便别人直接访问并改写


你应当在最后一步做这项工作,这里有一个文章,
交付代码前的5件事值的参考。
同后台交互 
JavaScript 是一门前台语言,你需要别的语言同后台交互,并返回数据,使用 AJAX,你可以让 JavaScript 直接使用同后台的交互,将复杂的数据处理交由后台处理。
JavaScript 框架 
自己编写适应各种浏览器的代码是完全浪费时间,应当选择一个 JavaScript 框架,让这些复杂的事情交给框架处理。

实用的 JavaScript 测试及效验工具

JavaScript 是一款强大的广泛运用于现代Web站点及应用的脚本语言。作为一个技艺精湛的 Web 开发者,掌握JavaScript可以增强用户的使用体验,提供交互及富客户端等功能。
尽管JavaScript 的语法非常简单,但对于写程序而言仍然是困难重重,就是因为它的运行环境:基于Web浏览器。

以下您可以看到收集的8个实用的 JavaScript 测试及效验工具,它们都可以在不同环境下进行单元测试及校验测试您的脚本。

JSLint 
JSLint是基于Web的验证JavaScript错误代码的工具。它拥有的功能及特定的设置来使用您的需求,自定义你的验证算法。

JsUnit 
JsUnit是一款在客户端(在浏览时)的单元测试JavaScript框架。对JavaScript而言,
JUnit就像是它的一个端口。当然它也可以在多 个浏览器、多个机器的不同操作系统中自动运行。它的发展始于2001年1月。

J3Unit 
J3Unit是一个面向对象的JavaScript单元测试框架。J3Unit在网页浏览器中直接运行JavaScript的测试,也可以自动运行 JUnit 和 Jetty。J3Unit是建立在JUint和Script.aculo.us的基础之上来更好地实现自动运行JavaScript 单元测试。面向对象的JavaScript单元测试是由Script.aculo.us的Test.Unit.Runner对象编写的,基于 prototype JavaScript库。

Crosscheck 
Crosscheck是一款开源的校验浏览器中的JavaScript测试框架。它可以帮助您在不同的浏览器中,诸如:
Internet ExplorerFirefox等,而不需要一 一安装他们来确认您的代码是否正确。您唯一需要的是必须要有Java虚拟机环境。

YUI Test 
YUI测试是一款基于浏览器,提供解决方案的测试框架。使用YUI,您可以方便地添加单元测试,寻求JavaScript解决方案。它是由 Yahoo! UI Library开发的一个JavaScriptMVC测试插件,能够让你模范大部分DOM动作,比如写,拖拽,比如模范AJAX响应,并且能够使用断言 (assertions)。它能够象函数一样运行,并且能够在不同的console窗口进行集成测试。虽然它不是在任何 xUnit 框架基础上开发而来,但YUI Test仍然有很多nUnit 和 JUnit的所具有的特性。( While not a direct port from any specific xUnit framework, YUI Test does derive some characteristics from nUnit and JUnit. 这段翻译得不好,但相信大致意思是对的)。

Regular Expression Tool 
Regular Expression Tool(正则表达式工具)是一款在线工具,用来测试您的正则表达式代码是否正确。当您想快速测试各种文本例子的正则表达式时非常得心应手。

JSLitmus 
JSLitmus是款轻量级的工具,用来测试JavaScript执行性能情况,采用直观的API。

JavaScript Regular Expression Tester 
这块便利的应用程序是在浏览器中使用JavaScript来测试JavaScript正则表达式的。操作界面跟其他正则表达式测试工具无异,不同的 是,它测试的是JavaScript正则表达式在JavaScript中的性能情况。

Ajax、Comet、HTML 5 Web Sockets技术比较分析

Ajax、Comet、HTML 5 Web Sockets技术比较分析


作者
Dionysios G. Synodinos   译者
沙晓兰


十年代中期,WWW以迅猛之势转眼跻身传播信息的主要渠道之一。浏览器的身影开始无处不在,用户也随之开始适应这种信息传播方式。显然,WWW提
供的应用平台能够赢得历史上任何一个平台都无法比及的用户量。但当时很难实现这样的目标是因为一些标准(HTML、HTTP等)都不很完善,这些标准设计
的时候都没有考虑到高度交互和富客户体验。最初的一些富在线应用基本上都是由Microsoft
Exchange开发组实现的。96年以来,他们曾采用IFrame为邮件服务器系统提供Outlook类型的前端应用。这些早期尝试在响应能力和整体的
用户体验方面都非常落后,但从这些应用身上却可以清楚地看到即将兴起的网络应用。1998年,团队开始为MS Exchange Server
2000编写web前端,他们开发了XMLHTTP,这个控件实现了单个web页面与服务器间的异步交互。可以看到,XMLHTTP实际上根本没有立即和
XML捆绑起来。XMLHTTP这个名字是Alex
Hopmann提出的,他是后来加入开发团队的,据说名字采用这个前缀的唯一的原因是IE5当时正在准备第二个beta版本,而这个控件必须作为这个版本
的MSXML库的一部分发布,这才冠上了XML。 

Mozilla基金会在2002年开发他们的浏览器的一个版本时,也以XMLHttpRequest的形式实现这一新技术,这个浏览器就是后来的
Firefox。尽管当时有一些商家也曾尝试运用这些新API,但他们采用的的这种远程脚本程序的模式一直没有引起公众的注意,直到Google开始部署
基于JavaScript和XHR的一系列新型服务。当时的第一个服务是2005年2月8日Google Blog上发布的Google
Maps。之后不久,XHR就一跃成为业界最炙手可热的话题。直到那时,也还没人预料到XHR给Web应用开发带来的革命性的推动,但它的成功开始让我们
转变之前对WWW的一些看法。 

Kaazing Gateway发布之际,InfoQ采访了Richard Smith,谈到关于AJAX, Comet以及蒸蒸日上的HTML 5 Web Sockets等技术的发展情况:

Ajax为HTTP通信模型提供了很好的解决方案,它在客户端异步轮询服务器端事件。服务器事件依次排列在待处理队列中,根据轮询时间隙依次传送到
浏览器,这样模拟服务器发起的通信,在轮询时间隙间进行实时消息传递。因此,仅仅依靠Ajax,我们永远都不可能实现真正的实时通信。 

Comet引入的优化针对的是HTTP通信初始之时,它在HTTP基础上采用“push”通信风格。Comet提供的几项技术能够在没有客户端发送
请求的前提下让服务器主动将信息发送到浏览器。如果再增加一个额外的HTTP连接的话,Comet甚至可以在两个HTTP连接上实现双向通信。但
Comet的绊脚石在于各个浏览器提供商对XHR、iFrames——这两种实现Comet所需的数据块的支持程度不尽相同,没有统一的实现标准。另外,
无论是从网络还是开发角度来看,Comet管理两个连接的开销都很大。这些开销带来的直接影响就是Comet应用中的传输延时,限制了它们所提供的实时通
信的精确性。

HTML 5 WebSocket代表的是Comet和Ajax推进HTTP通信新一轮的尝试。HTML 5
WebSocket规范中定义,在浏览器和服务器之间采用单socket全双工(或者叫双向)传输来push和pull信息。这不但可以避免Comet中
存在的连接和可移植问题,还能够提供比Ajax轮询更高效的解决方案。目前,HTML 5 WebSocket是推动web全双工实时通信的主要机制。

Richard认为,AJAX和Comet这两种方式都有各自的局限:

要通过Ajax来模拟服务器端起始的通信就需要轮询机制,而这一机制不顾应用的状态改变盲目检查更新,结果就是CPU周期和内存毫无必要地过早或者
太晚侦测服务器的更新,客户和服务器两端的资源利用状况因此都相当差劲。所以,传统的Ajax应用必须根据服务器上事件的发布率不停地调整轮询时间隙的长
短,才能改善各个请求的准确度。另外,高频率轮询会加重网络承载,拖累服务器;低频率轮询又会错过更新和传送一些失去时效的信息。无论哪种情况,消息传递
过程都无法避免传输延时。短间隙轮询开销很大,因为要支持这样的小型服务器ping需要大量服务器资源。 

Comet维护服务器和浏览器之间的持续连接和长时间有效的HTTP请求,以此来尝试“push”型通信。这种连接下,服务器可以发送事件,但连接
是由客户端浏览器发起。逆流请
求也可以看做是浏览器向服务器发送的请求,这需要一个额外的HTTP连接。Comet因此可以利用跨两个HTTP连接的双向通信。但是,维护这两个连接会
消耗服务器端大量资源,因为这也意味着服务一个顾客需要消耗双倍资源。而且,浏览器的配置往往都是通过域来限制HTTP连接。Comet的应用因此更为复
杂,还常常会要求运用一些像多路复用那样复杂的技术,再或者就是要管理多个域。 

有一些Comet解决方案试图降低长轮询技术导致的资源消耗,但这一技术发送太多的HTTP请求/回复头信息。比如,服务器发送的
每个事件,都通过客户浏览器为连接提供服务,这迫使浏览器必须和服务器重新建立连接。这一动作又引发了另一个客户端请求以及服务器在长轮询时间隙中发送回
复。很多时候,回复中的HTTP头内容完全超过了传送的信息。

From client (browser) to server:
GET /long-polling HTTP/1.1rn
Host: www.kaazing.comrn
User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9) Gecko/2008061017 Firefox/3.0rn
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8rn
Accept-Language: en-us,en;q=0.5rn
Accept-Encoding: gzip,deflatern
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7rn
Keep-Alive: 300rn
Connection: keep-alivern
Cache-Control: max-age=0rn
rn


From server to client (browser):
Date: Tue, 16 Aug 2008 00:00:00 GMTrn
Server: Apache/2.2.9 (Unix)rn
Content-Type: text/plainrn
Content-Length: 12rn
rn
Hello, world

用Comet开发有很多挑战。实现你自己的解决方案不是不可能,但这需要开发JavaScript类库,借用frame和XHR
Streaming等很多技术来维护持续连接。这时候,问题就在于不同的浏览器对这些技术有不同的实现,更糟的是,它们往往都依赖服务器端代码推送
JavaScript代码段,不仅增加了整个实现的复杂程度,还引入了移植性的问题。 

还好,现在有几个框架提供了这些传输的抽象,简化了Comet开发。其中最著名的就是SitePen的Cometd,其实现完全参
考Bayuex规范。Bayuex规范定义了Comet的publish-subscribe模型。Jetty近期版本也包含了基于Java的服务器端的
Bayeux实现。 

Bayuex和Cometd着实简化了Comet,然而它的API和wire协议还是有很多争议。Comet Daily上有一个 “Coliding Comet: Battle of the Bayuex”系列就专门深入讨论关于Bayuex的各种问题。

Richard还认为 HTML 5 WebSockets可以从当前的各种解决方案中获得很多帮助:

尽管Comet和Ajax都可以实现提供桌面应用功能的终端用户体验,而且传输延时也可以缩短到用户无法感知的程度,但仍然只有Web
Sockets才能真正为浏览器提供精确、高效的流事件,保证传输延时可以微乎其微,直至忽略不计。这是目前为止通过web发送实时信息最出色的解决方
案。它不仅通过单个TCP/IP连接提供完整的异步双工道流通信,而且新的HTTP头的应用也非常有利,更重要的是它能够支持浏览器和源服务的消息采用同
样的格式。 

多数Comet实现都依赖Bayeux协议。该协议要求源服务发出的消息必须转换成Bayeux协议支持的格式,这一并不必要的转
换反而使得整个系统更加复杂,开发员不得不在服务器端处理一种消息格式(比如JMS、IMAP、XMPP等),在客户端又要处理另一种消息格式(比如
Bayeux、JSON)。而且实现将源协议转换到Bayeux的代码硬是要在发送消息之前对消息本身进行解析和处理,这又给系统增添了不必要的性能负
载。采用Web Sockets的话,就不会有因为转换代码而增加系统的复杂性,也就不用为这方面的性能担忧。 

WebSockets经常遇到的一个问题是它是否可行。目前来看,浏览器本身没法直接支持这项技术。但再过几个月就肯定可以了,像
WebKit、Firefox和Opera这样的浏览器从来就对HTML
5的特性——比如Canvas、postMessage、离线存储和服务器端发送信息(SSE)等反应迅速,及时添加相应的支持。 

WebSockets还需要服务端一定程度的支持,因为现存HTTP连接更新到新连接需要HTTP的一个起始“握手”。 Kaazing
Gateway开源项目实现了第一个支持这一动作的服务器,并且拥有能够支持成千上万持续连接所需的扩展性。 Kaazing
Gateway的供应商Kaazing还提供了一个可以让当前所有web浏览器都支持WebSockets的JavaScript类库。所以,目前对
WebSocket的支持也可说是准备就绪了。 

为了支持HTML 5 WebSockets,Kaazing发布了Kaazing Gateway 8.09_2 Atlantis,这是一个开源HTML 5 WebSocket服务器,可以在Mozilla公共许可的衍生许可—— OSI approved Common Public Attribution License (CPAL)下使用:

Kaazing Gateway提供JavaScript类库来模拟HTML 5 WebSocket,开发员现在就可以开始运用WebSockets,结合WebSocket接口创建的应用在当前甚或是未来的浏览器上都可以部署。 

Kaazing Gateway背后的超高性能服务器的单个节点能够支持成千上万的并发连接。多实例通过传统的HTTP负载平衡或者DNS
round robin算法集群分类,因此能够支持无数个持续客户连接。除了大量的连接之外,Kaazing
Gateway的高性能和分级事件驱动构架(SEDA)还推动它本身能够处理高数据吞吐量。 

Kaazing Gateway的Atlantis发布还为流行的消息服务(诸如Apache
ActiveMQ、RabbittMQ)和XMPP服务(诸如OpenFire、Jabberd和其它一些流行的聊天服务器)打包了JavaScript
客户端。这样,创建那些基于web的聊天应用或是stock matrixes、网上交易平台、在线游戏等消息发送应用就更为简单了。

计划中的Kaazing Gateway 8.12发布把目标瞄准了更多的HTML 5特性,例如服务器端发送事件(Server-sent Event)、更先进的安全服务,以及对XMPPJabber)、STOMP (比如ActiveMQ、RabbitMQOpenMQ)等的扩展支持:

它所提供的类库能够让目前的浏览器都支持HTML 5服务器发送事件,引进了HTML 5
postMessage的支持,无疑方便了跨文档的消息传递。Kaazing的HTML 5类库还包括对HTML
5离线存储的支持,提供简易、基于DOM的存储解决方案。Kaazing
Gateway及其客户类库现在还为跨站请求支持W3C访问控制,这一机制能够让客户端启动跨站请求,比较多的提法是跨站
XmlHttpRequests。 

除了对HTML 5的扩展支持以外,Kaazing Gateway
8.12还提供更高级的XMPP特性,比如群聊。这一发布版本还引进了STOMP-JMS适配器,因此,结合Kaazing
Gateway还能适配任何现存的Java消息服务(JMS)(例如JBoss Messaging、Tibco
EMS、OpenMQ、SwiftMQ、WebSphere MQ等等)。

查看英文原文:HTML 5 Web Sockets vs. Comet and Ajax

Dojo Toolkit 1.2 RC1发布

据国外媒体报道,9月22日消息,Dojo团队发布了Dojo Toolkit 1.2 RC1。Dojo是一个非常强大的面向对象的JavaScript的工具箱,它为开发Web胖客户端程序提供了一套完整的Widget和一些特效操作。
新版本改进:
1、在1.1.1版本基础上增加了一千个多个更新、改进和错误修正。
2、大量 Dijit 改进(Dijit是一个基于Dojo的widget库,使用Dijit可以大大简化Web 2.0界面的开发),包括使用.attr()来获得和设置所有的属性,所有的Widgets性能和流畅性都得到极大提升。
3、Dojo Grid做了极大改进,完全支持dojo.data集成
4、绘图功能增强,包括tooltips、animations、events、legends和几个新的绘图类型。
5、doh.robot使用 Selenium 和 Windmill 提供单元测试集成
新功能还包括但不限于下面所列出的:
多文件上传;
优化图片widgets(Lightbox, Slideshow等);
快速的XML解析;
多种数据存储方式;
快速包含Google analytics;
提升windowName和dojox.secure的安全性;
JSON(Path, Query, Referencing, and Schema);

JavaScript多线程编程简介

作者 Daisuke Maki译者 张凯峰

虽然有越来越多的网站在应用AJAX技术进行开发,但是构建一个复杂的AJAX应用仍然是一个难题。造成这些困难的主要原因是什么呢?是与服务器的异步通信问题?还是GUI程序设计问题呢?通常这两项工作都是由桌面程序来完成的,那究竟为何开发一个可以实现同样功能的AJAX应用就这么困难呢?

相关厂商内容

SOY Framework:Java富客户端快速开发框架

迷你书免费下载:开源选型、Struts 2、Scrum和XP、GRails

AJAX 开发中的难题

让我们通过一个简单的例子来认识这个问题。假设你要建立一个树形结构的公告栏系统(BBS),它可以根据用户请求与服务器进行交互,动态加载每篇文章的信息,而不是一次性从服务器载入所有文章信息。每篇文章有四个相关属性:系统中可以作为唯一标识的ID、发贴人姓名、文章内容以及包含其所有子文章 ID的数组信息。首先假定有一个名为getArticle()的函数可以加载一篇文章信息。该函数接收的参数是要加载文章的ID,通过它可从服务器获取文章信息。它返回的对象包含与文章相关的四条属性:id,name,content和children。例程如下:

function ( id ) {
var a = getArticle(id);
document.writeln(a.name + "
" + a.content);
}

然而你也许会注意到,重复用同一个文章ID调用此函数,需要与服务器之间进行反复且无益的通信。想要解决这个问题,可以考虑使用函数 getArticleWithCache(),它相当于一个带有缓存能力的getArticle()。在这个例子中,getArticle()返回的数据只是作为一个全局变量被保存下来:

var cache = {};
function getArticleWithCache ( id ) {
if ( !cache[id] ) {
cache[id] = getArticle(id);
}
return cache[id];
}

现在已将读入的文章缓存起来,让我们再来考虑一下函数backgroundLoad(),它应用我们上面提到的缓存机制加载所有文章信息。其用途是,当读者在阅读某篇文章时,从后台预加载它所有子文章。因为文章数据是树状结构的,所以很容易写一个递归的算法来遍历树并且加载所有的文章:

function backgroundLoad ( ids ) {
for ( var i=0; i < ids.length; i++ ) {
var a = getArticleWithCache(ids[i]);
backgroundLoad(a.children);
}
}

backgroundLoad ()函数接收一个ID数组作为参数,然后通过每个ID调用前面定义过的getArticldWithCache()方法,这样就把每个ID对应的文章缓存起来。之后再通过已加载文章的子文章ID数组递归调用backgroundLoad()方法,如此整个文章树就被缓存起来。

到目前为止,一切似乎看起来都很完美。然而,只要你有过开发AJAX应用的经验,你就应该知晓这种幼稚的实现方法根本不会成功,这个例子成立的基础是默认 getArticle()用的是同步通信。可是,作为一条基本原则,JavaScript要求在与服务器进行交互时要用异步通信,因为它是单线程的。就简单性而言,把每一件事情(包括GUI事件和渲染)都放在一个线程里来处理是一个很好的程序模型,因为这样就无需再考虑线程同步这些复杂问题。另一方面,他也暴露了应用开发中的一个严重问题,单线程环境看起来对用户请求响应迅速,但是当线程忙于处理其它事情时(比如说调用getArticle()),就不能对用户的鼠标点击和键盘操作做出响应。

如果在这个单线程环境里进行同步通信会发生什么事情呢?同步通信会中断浏览器的执行直至获得通信结果。在等待通信结果的过程中,由于服务器的调用还没有完成,线程会停止响应用户并保持锁定状态直到调用返回。因为这个原因,当浏览器在等待服务器响应时它不能对用户行为作出响应,所以看起来像是冻结了。当执行 getArticleWithCache()和backgroundLoad()会有同样的问题,因为它们都是基于getArticle()函数的。由于下载所有的文章可能会耗费很可观的一段时间,因此对于backgroundLoad()函数来说,浏览器在此段时间内的冻结就是一个很严重的问题——既然浏览器都已经冻结,当用户正在阅读文章时就不可能首先去执行后台预加载数据,如果这样做连当前的文章都没办法读。

如上所述,既然同步通信在使用中会造成如此严重的问题,JavaScript就把异步通信作为一条基本原则。因此,我们可以基于异步通信改写上面的程序。 JavaScript要求以一种事件驱动的程序设计方式来写异步通信程序。在很多场合中,你都必须指定一个回调程序,一旦收到通信响应,这个函数就会被调用。例如,上面定义的getArticleWithCache()可以写成这样:

var cache = {};
function getArticleWithCache ( id, callback ) {
if ( !cache[id] ) {
callback(cache[id]);
} else {
getArticle(id, function( a ){
cache[id] = a;
callback(a);
});
}
}

这个程序也在内部调用了getArticle()函数。然而需要注意的是,为异步通信设计的这版getArticle()函数要接收一个函数作为第二个参数。当调用这个getArticle()函数时,与从前一样要给服务器发送一个请求,不同的是,现在函数会迅速返回而非等待服务器的响应。这意味着,当执行权交回给调用程序时,还没有得到服务器的响应。如此一来,线程就可以去执行其它任务直至获得服务器响应,并在此时调用回调函数。一旦得到服务器响应, getArticle()的第二个参数作为预先定义的回调函数就要被调用,服务器的返回值即为其参数。同样的,getArticleWithCache ()也要做些改变,定义一个回调参数作为其第二个参数。这个回调函数将在被传给getArticle()的回调函数中调用,因而它可以在服务器通信结束后被执行。

单是上面这些改动你可能已经认为相当复杂了,但是对backgroundLoad()函数做得改动将会更复杂,它也要被改写成可以处理回调函数的形式:

function backgroundLoad ( ids, callback ) {
var i = 0;
function l ( ) {
if ( i < ids.length ) {
getArticleWithCache(ids[i++], function( a ){
backgroundLoad(a.children, l);
});
} else {
callback();
}
}
l();
}

改动后的backgroundLoad()函数看上去和我们以前的那个函数已经相去甚远,不过他们所实现的功能并无二致。这意味着这两个函数都接受 ID数组作为参数,对于数组里的每个元素都要调用getArticleWithCache(),再应用已经获得子文章ID递归调用 backgroundLoad ()。不过同样是对数组的循环访问,新函数中的就不太好辨认了,以前的程序中是用一个for循环语句完成的。为什么实现同样功能的两套函数是如此的大相径庭呢?

这个差异源于一个事实:任何函数在遇到有需要同服务器进行通信情况后,都必须立刻返回,例如getArticleWithCache()。除非原来的函数不在执行当中,否则应当接受服务器响应的回调函数都不能被调用。对于JavaScript,在循环过程中中断程序并在稍后从这个断点继续开始执行程序是不可能的,例如一个for语句。因此,本例利用递归传递回调函数实现循环结构而非一个传统循环语句。对那些熟悉连续传送风格(CPS)的人来说,这就是一个 CPS的手动实现,因为不能使用循环语法,所以即便如前面提到的遍历树那么简单的程序也得写得很复杂。与事件驱动程序设计相关的问题是控制流问题:循环和其它控制流表达式可能比较难理解。

这里还有另外一个问题:如果你把一个没有应用异步通信的函数转换为一个使用异步通信的函数,那么重写的函数将需要一个回调函数作为新增参数,这为已经存在的APIs造成了很大问题,因为内在的改变没有把影响限于内部,而是导致整体混乱的APIs以及API的其它使用者的改变。

造成这些问题目的根本原因是什么呢?没错,正是JavaScript单线程机制导致了这些问题。在单线程里执行异步通信需要事件驱动程序设计和复杂的语句。如果当程序在等待服务器的响应时,有另外一个线程可以来处理用户请求,那么上述复杂技术就不需要了。

试试多线程编程

让我来介绍一下Concurrent.Thread,它是一个允许JavaScript进行多线程编程的库,应用它可以大大缓解上文提及的在 AJAX开发中与异步通信相关的困难。这是一个用JavaScript写成的免费的软件库,使用它的前提是遵守Mozilla Public License和GNU General Public License这两个协议。你可以从他们的网站 下载源代码。

马上来下载和使用源码吧!假定你已经将下载的源码保存到一个名为Concurrent.Thread.js的文件夹里,在进行任何操作之前,先运行如下程序,这是一个很简单的功能实现:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
<script type="text/javascript">
Concurrent.Thread.create(function(){
var i = 0;
while ( 1 ) {
document.body.innerHTML += i++ + "<br>";
}
});
</script>

执行这个程序将会顺序显示从0开始的数字,它们一个接一个出现,你可以滚屏来看它。现在让我们来仔细研究一下代码,他应用while(1)条件制造了一个不会中止的循环,通常情况下,象这样不断使用一个并且是唯一一个线程的JavaScript程序会导致浏览器看起来象冻结了一样,自然也就不会允许你滚屏。那么为什么上面的这段程序允许你这么做呢?关键之处在于while(1)上面的那条Concurrent.Thread.create()语句,这是这个库提供的一个方法,它可以创建一个新线程。被当做参数传入的函数在这个新线程里执行,让我们对程序做如下微调:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
<script type="text/javascript">
function f ( i ){
while ( 1 ) {
document.body.innerHTML += i++ + "<br>";
}
}
Concurrent.Thread.create(f, 0);
Concurrent.Thread.create(f, 100000);
</script>

在这个程序里有个新函数f()可以重复显示数字,它是在程序段起始定义的,接着以f()为参数调用了两次create()方法,传给create()方法的第二个参数将会不加修改地传给f()。执行这个程序,先会看到一些从0开始的小数,接着是一些从100,000开始的大数,然后又是接着前面小数顺序的数字。你可以观察到程序在交替显示小数和大数,这说明两个线程在同时运行。

让我来展示Concurrent.Thread的另外一个用法。上面的例子调用create()方法来创建新线程。不调用库里的任何APIs也有可能实现这个目的。例如,前面那个例子可以这样写:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
<script type="text/x-script.multithreaded-js">
var i = 1;
while ( 1 ) {
document.body.innerHTML += i++ + "<br>";
}
</script>

在script 标签内,很简单地用JavaScript写了一个无穷循环。你应该注意到标签内的type属性,那里是一个很陌生的值(text/x- script.multithreaded-js),如果这个属性被放在script标签内,那么Concurrent.Thread就会在一个新的线程内执行标签之间的程序。你应当记住一点,在本例一样,必须将Concurrent.Thread库包含进来。

有了Concurrent.Thread,就有可能自如的将执行环境在线程之间进行切换,即使你的程序很长、连续性很强。我们可以简要地讨论下如何执行这种操作。简言之,需要进行代码转换。粗略地讲,首先要把传递给create()的函数转换成一个字符串,接着改写直至它可以被分批分次执行。然后这些程序可以依照调度程序逐步执行。调度程序负责协调多线程,换句话说,它可以在适当的时候做出调整以便每一个修改后的函数都会得到同等机会运行。 Concurrent.Thread实际上并没有创建新的线程,仅仅是在原本单线程的基础上模拟了一个多线程环境。

虽然转换后的函数看起来是运行在不同的线程内,但是实际上只有一个线程在做这所有的事情。在转换后的函数内执行同步通信仍然会造成浏览器冻结,你也许会认为以前的那些问题根本就没有解决。不过你不必耽心,Concurrent.Thread提供了一个应用JavaScript 的异步通信方式实现的定制通信库,它被设计成当一个线程在等待服务器的响应时允许其它线程运行。这个通信库存于 Concurrent.Thread.Http下。它的用法如下所示:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
<script type="text/x-script.multithreaded-js">
var req = Concurrent.Thread.Http.get(url, ["Accept", "*"]);
if (req.status == 200) {
alert(req.responseText);
} else {
alert(req.statusText);
}
</script>

get()方法,就像它的名字暗示的那样,可以通过HTTP的GET方法获得指定URL的内容,它将目标URL作为第一个参数,将一个代表HTTP 请求头的数组作为可选的第二个参数。get()方法与服务器交互,当得到服务器的响应后就返回一个XMLHttpRequest对象作为返回值。当 get()方法返回时,已经收到了服务器响应,所以就没必要再用回调函数接收结果。自然,也不必再耽心当程序等待服务器的响应时浏览器冻结的情况了。另外,还有一个 post()方法可以用来发送数据到服务器:

<script type="text/javascript" src="Concurrent.Thread.js"></script>
<script type="text/x-script.multithreaded-js">
var req = Concurrent.Thread.Http.post(url, "key1=val1&key2=val2");
alert(req.statusText);
</script>

post()方法将目的URL作为第一个参数,要发送的内容作为第二个参数。像get()方法那样,你也可以将请求头作为可选的第三个参数。

如果你用这个通信库实现了第一个例子当中的getArticle()方法,那么你很快就能应用文章开头示例的那种简单的方法写出 getArticleWithCache(),backgroundLoad ()以及其它调用了getArticle()方法的函数了。即使是那版backgroundLoad()正在读文章数据,照例还有另外一个线程可以对用户请求做出响应,浏览器因此也不会冻结。现在,你能理解在JavaScript中应用多线程有多实用了?

想了解更多

我向你介绍了一个可以在JavaScript中应用多线程的库:Concurrent.Thread。这篇文章的内容只是很初级的东西,如果你想更深入的了解,我推荐您去看the tutorial。它提供有关Concurrent.Thread用法的更多内容,并列出了可供高级用户使用的文档,是最适合起步的材料。访问他们的网站也不错,那里提供更多信息。

有关作者

Daisuke Maki:从 International Christian大学文科学院自然科学分部毕业后(取得文学学士学位),又在Electro-Communications大学的研究生院信息专业攻读硕士学位。擅长Web开发和应用JavaScript的AJAX。他开发了Concurrent.Thread。2006财政年度在日本信息技术促进机构(IPA)指导的项目Explatory Software Project中应用了这个设计。

目前已经拥有一个工学硕士学位的他正在Electro-Communications大学的研究生院注册攻读博士学位。

如何选择Javascript框架(Javascript Framework)

Ajax的兴起,给Javascript带来了新的生机,大量的javascript框架(Javascript Framework)层出不穷,一些框架来至于开发人员项目经验的总结和提炼,也有一些框架来至于商业公司,同时以开源和商业两种方式发布。借助这些框架,可以大大加速Ajax项目的开发速度,但同时也面临不同的学习曲线,以及架构扩展性等等问题。如何选择Javascript框架,成为开发人员和架构师头痛的一个问题。如果你正面临这样的问题,希望下面的几个建议对你在选择javascript框架上会有所帮助。

你的项目需求是什么

首先要问自己这是一个什么项目,具体的需求是什么,是一个普通类型的网站还是一个在线的web应用程序,是否需要处理大量的键盘和鼠标事件,是否需要给用户各类高级的ajax特性,还是说只要实现一个简单的异步页面刷新和一些简单dom操作,如果是后者,则可以选择一个相对简单的javascript框架,封装基本的xmlhttprequest操作和dom操作就足够了。

浏览器的支持情况

不同的框架兼容的浏览器会有所不同,尤其是一些高级的javascript框架,对低版本的浏览器都不支持,还有一些框架只支持ie和firefox,对其他浏览器如opera、safari不支持。所以在框架的选择上还要考虑到系统的目标用户,如果目标用户都只使用ie6.0以上浏览器,那么在框架的选择上余地就更大了。

框架后面是否有一个核心的开发团队

很多框架往往都是个人在业务时间开发的,随时可能停止更新,而如果后面有一个团队,则可以在一定程度上保准代码的更新,对bug和一些问题的及时响应,同时在代码质量上也相对有保准。

框架的成熟度

如果一个新的框架刚刚发布,使用的人往往不多,如果你贸然采用,在使用过程中遇到问题,可能要找个能帮你解决问题的人或者在网上找资料都显的很难。所以在这方面也要有所考虑。

框架的发布更新频率

一个框架有很高的发布更新频率说明新的功能在不断加入或者bug被fix的速度很快,反之一个框架半年都不出一个版本,基本可以说明这个框架已经不被开发者重视,很难得到新的发展。

文档的友好性

一个框架尤其是相对比较复杂的框架,如果没有充分和友好的文档,学习曲线会比较高,使用者在使用过程中往往需要通过阅读代码和其他外部的文章来学习怎么使用和解决一些问题。所以文档也是很重要的一个因素。

是否有个活跃的社区

一些成功的开源框架背后往往有一个社区在支撑,大家在里面交流使用经验,互相帮助解决使用过程中遇到的问题。任何问题,只要在这类社区中寻求帮助,往往很快就可以得到他人的帮助。这样的框架,即使一开始不是很成熟,也会很快发展起来。

框架的扩展性

在实际的项目过程中,往往一个特定的框架是很难直接满足你的所有需求的,这就要求你需要去做一些定制和扩展的工作,如果一个框架没有很好的扩展性,则你可能在项目后期为了实现某个特定的需求,不得不采用另一个新的框架,大大加大了项目的成本。所以选择一个有很好扩展性,如支持plugin等机制的框架,对你今后系统的扩展会有很大的帮助。

性能和网络环境

不同的系统在性能和功能的侧重上会有所不同,比如一个基于互联网的项目,可能考虑更多的是要求在保准性能的前提下,再来讲功能,很多高级的javascript框架往往在性能上不能让人满意,一部分原因是封装了太多功能,导致js文件会非常大,在互联网环境下,下载这个js文件就会耗去不少时间,还有就是为了保准如框架的扩展性,往往做了多层封装和抽象,在某种程度上其实是以牺牲部分性能为代价的。所以这样的框架可能更适合一些intranet内部的项目,而不是基于互联网的项目。

jQuery入门简介

jQuery是最近比较火的一个JavaScript库,从del.icio.us/上相关的收藏可见一斑。

到目前为之jQuery已经发布到1.2.1版本,而在这之前的一个星期他们刚发布1.2版本,看看他的各个版本的 发布时间 ,不难发现他的飞速发展,每个月都在更新版本;而且不断有人开发出新的 jQuery插件 ,最近又推出了 jQuery UI 库。

jQuery于2006年一月十四号在BarCamp NYC (New York City)面世。主将 John Resig ,写有《Pro JavaScript Techniques》一书,因为效力于mozolla,据说firefox 3将包含Jquery,现在的 Jquery团队 有主要开发人员,推广人员,UI,插件开发,网站设计维护,其中3个主要开发人员分别是:两个美国人John Resig/Brandon Aaron,一个德国人Jorn Zaefferer)

下面简单介绍一下jQuery的一些特性和用法:
1.精准简单的选择对象(dom):

$(‘#element’);// 相当于document.getElementById("element")
$(‘.element’);//Class
$(‘p’);//html标签
$("form > input");//子对象
$("div,span,p.myClass");//同时选择多种对象
$("tr:odd").css("background-color", "#bbbbff");//表格的隔行背景
$(":input");//表单对象
$("input[name=’newsletter’]");//特定的表单对象

2.对象函数的应用简单和不限制:

3.对已选择对象的操作(包括样式):

$("#element").addClass("selected");//给对象添加样式
$(‘#element’).css({ "background-color":"yellow", "font-weight":"bolder" });//改变对象样式
$("p").text("Some new text.");//改变对象文本
$("img").attr({ src: "test.jpg", alt: "Test Image" });//改变对象文本
$("p").add("span");//给对象增加标签
$("p").find("span");//查找对象内部的对应元素
$("p").parent();//对象的父级元素
$("p").append("<b>Hello</b>");//给对象添加内容

4.支持aJax,支持文件格式:xml/html/script/json/jsonp

$("#feeds").load("feeds.html");//相应区域导入静态页内容
$("#feeds").load("feeds.php", {limit: 25}, function(){alert("The last 25 entries in the feed have been loaded");});//导入动态内容

4.对事件的支持:

$("p").hover(function () {
      $(this).addClass("hilite");//鼠标放上去时
    }, function () {
      $(this).removeClass("hilite");//移开鼠标
    });//鼠标放上去和移开的不同效果(自动循环所有p对象

5.动画:

$("p").show("slow");//隐藏对象(慢速渐变)
$("#go").click(function(){
$("#block").animate({
    width: "90%",
    height: "100%",
    fontSize: "10em"
}, 1000 );
});//鼠标点击后宽、高、字体的动态变化

6.扩展:

$.fn.background = function(bg){
    return this.css(‘background’, bg);
};
$(#element).background("red");

如果要为每一个jQuery 对象添加一个函数,必须把该函数指派给 $.fn,同时这个函数必须要返回一个 this(jQuery 对象)

jQuery相关

  1. 《Learning jQuery:Better Interaction Design and Web Development with Simple JavaScript Techniques》第一本由jQuery的开发人员写的关于如何学习jQuery的书已经于七月面世,同时还有三本相关的书在问世当中。
  2. jQueryCamp 2007,一个jQuery开发人员的见面会也将于10月27日在Boston召开。
  3. VisualJquery是一个Jquery的学习和查询网站,也跟着更新到了1.1.2版本。

jQuery官网介绍翻译:

jQuery是一个以前未曾有过的JavaScript库。
他快速、简洁,能够很轻易地处理HTML文档、控制事件、给页面添加动画和Ajax效果。

jQuery是为了改变JavaScript的编写方式而设计的。

  • 他适合所有人:设计师、开发人员、极客、商业应用…
  • 体积小:26.5KB(1.2.1压缩版),45.3KB(1.2.1精简版),78.6KB(1.2.1完整版)…20.7KB(1.1.2压缩版),57.9KB(1.1.2完整版)
  • 兼容性:支持CSS 1-3和基本的XPath
  • 跨浏览器:IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+(向后兼容)

jQuery插件

Ajax (25)/Animation and Effects (26)/Browser Tweaks (6)/Data (17)/DOM (21)/Drag-and-Drop (6)/Events (19)/Forms (39)/Integration (12)/JavaScript (20)/jQuery Extensions (37)/Layout (23)/Media (13)/Menus (13)/Navigation (23)/Tables (11)/User Interface (84)/Utilities (27)/Widgets (41)/Windows and Overlays (4)

jQueryUI库

基本的鼠标互动:拖拽(drag and dropping)、排序(sorting)、选择(selecting)、缩放(resizing)
各种互动效果:手风琴式的折叠菜单(accordions)、日历(date pickers)、对话框(dialogs)、滑动条(sliders)、表格排序(table sorters)、页签(tabs)、放大镜效果(magnifier)、阴影效果(shadow)