概述

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 去掉了其中的一些实现,并提出了ECMAScript-262标准,并确定 Javascript 的正式名字为 ECMAScript,但是 JavaScript 的名字已经深入人心,故本书中仍沿用 Javascript 这个名字。

动态网页

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

而当 AJAX 流行起来之后,更多的非常绚丽的 WEB 应用涌现了,而且呈越来越多的趋势,如 Gmail,Google Map,Google Reader,Remember the milk,facebook 等等优秀的 WEB2.0 应用,都大量的使用了 JavaScript 及其衍生的技术 AJAX。

JavaScript 语言特性

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

动态性

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

Js 代码

//定义一个对象  
var obj = new Object();  
//动态创建属性name  
obj.name = "an object";  
//动态创建属性sayHi  
obj.sayHi = function(){  
return "Hi";  
}  
obj.sayHi();

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

Js 代码

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());  

动态性是非常有用的,这个我们在第三章会详细讲解。

弱类型

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

Js代码

//定义一个变量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 的变量更像是一个容器,类似与 Java 语言中的顶层对象 Object,它可以是任何类型,解释器会根据上下文自动对其造型。

弱类型的好处在于,一个变量可以很大程度的进行复用,比如 String 类型的 name 字段,在被使用后,可以赋值为另一个 Number 型的对象,而无需重新创建一个新的变量。不过,弱类型也有其不利的一面,比如在开发面向对象的 Javascript 的时候,没有类型的判断将会是比较麻烦的问题,不过我们可以通过别的途径来解决此问题。

解释与编译

通常来说,Javascript 是一门解释型的语言,特别是在浏览器中的 Javascript,所有的主流浏览器都将Javascript 作为一个解释型的脚本来进行解析,然而,这并非定则,在 Java 版的 Javascript 解释器 rhino 中,脚本是可以被编译为 Java 字节码的。

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

Javascript 应用范围

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

客户端 Javascript

客户端的 JavaScript 随着 AJAX 技术的复兴,越来越凸显了 Javascript 的特点,也有越来越多的开发人员开始进行 JavaScript 的学习,使用 Javascript,你可以使你的 WEB 页面更加生动,通过 AJAX,无刷新的更新页面内容,可以大大的提高用户体验,随着大量的 JavaScript 包如 jQuery, ExtJS,Mootools 等的涌现,越来越多的绚丽,高体验的 WEB 应用被开发出来,这些都离不来幕后的 JavaScript 的支持。

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

我们可以看几个例子,来说明客户端的 JavaScript 的应用程度:

图 ExtJS 实现的一个网络相册,ExtJS 是一个非常优秀的 JavaScriipt 库

图 ExtJS 实现的一个表格,具有排序,编辑等功能

当然,客户端的 JavaScript 各有侧重,jQuery 以功能见长,通过选择器,可以完成 80% 的页面开发工作,并且提供强大的插件机制,下图为 jQuery 的 UI 插件:

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

服务端 Javascript

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

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

<html>  
<head>  
<script src="http://code.jquery.com/jquery.js" runat="both"></script>  
<script>  
jQuery(function($){  
$("form").submit(function(){  
save( $("textarea").val() );  
return false;  
});  
});  
</script>  
<script runat="server">  
function save( text ){  
Jaxer.File.write("tmp.txt", text);  
}  
save.proxy = true;  
function load(){  
$("textarea").val(  
Jaxer.File.exists("tmp.txt") ? Jaxer.File.read("tmp.txt") : "");  
}  
</script>  
</head>  
<body onserverload="load()">  
<form action="" method="post">  
<textarea></textarea>  
<input type="submit"/>  
</form>  
</body>  
</html>  

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

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

<script runat="server">  
var rs = Jaxer.DB.execute("SELECT * FROM table");  
var field = rs.rows[0].field;  
</script>  

通过动态,灵活的语法,再加上对原生的资源(如数据库,文件,网络等)操作的支持,服务器端的 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

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

图 sTodo 一个使用 JavaScript 来提供插件机制的 Java 桌面应用

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

著名的 Adobe reader 也支持 JavaScript 扩展,并提供 JavaScript 的 API 来访问 PDF 文档内容,可以通过 JavaScript 来定制 Adobe Reader 的界面以及功能等。

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 个菜单项,如图:

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

可见,JavaScript 在其他方面的也得到了广泛的应用。