JavaScript内核系列 第1章 前言及概述(转自javaeye)
前言
从2006年第一次接触JavaScript至今,算来也有四年时间了。上大学的时候,有一段时间沉迷于函数式编程,而那时候对JavaScript的理解仅仅局限在这是用来做网页的一种脚本,那些很恶心的弹出窗口和不断在页面上漂浮的黄色广告就是用JavaScript做出来的。工作之后,由于我们产品本身就是基于WEB展现的,而这个时候Ajax已经复兴数年了,但是于我,则只有“Ajax可以实现页面的局部刷新”这样一个概念。在实际的应用中,我才有机会面对真正的JavaScript,早期关于函数式编程的概念开始在我的记忆力复苏,而真正将JavaScript比较熟练的应用在产品的开发中,那已经是09年年末了。
开始研究JavaScript,是从2008年的5月左右,我从当当上买了一本犀牛书,并下载到一本蝴蝶书,结合着实际工作的的要求,一步一步的开始了JavaScript的探索。刚开始也只是被陷入在DOM的泥潭中,慢慢的开始考虑更进一步的东西,比如JSON作为数据交换,window对象的一些特殊的方法如eval等等,事实上仍然是陷入在语法的细节中。当终于有一天,理解了变量的作用域,理解了闭包,理解了函数的一等性,以往用来类比的来源于其他语言的所有概念则完全被颠覆了,当理解了JavaScript中的这些基本概念(虽然难以理解,但是它们的确是基本概念)之后,在回顾来时的路,则有些一览众山小的意思。
但是,这些看似基本的概念,在初学时如果混入了其他语言的概念以进行类比,则后患无穷,所以,学习JavaScript其实就一句话:“将JavaScript当成一门新的语言来学习”。鉴于我学习JavaScript的历程中,遇到了很多问题,但是最终都一一克服了,在这个过程中,有些心得,有些体会,就分节贴出来,做成一个系列,以方便初学JavaScript的朋友们。
第一章 概述
1.1 Javascript简史
在20世纪90年代,也就是早期的WEB站点上,所有的网页内容都是静态的,所谓静态是指,除了点击超链接,你无法通过任何方式同页面进行交互,比如让页面元素接受事件,修改字体等。人们于是迫切的需要一种方式来打破这个局限,于是到了1996年,网景(Netscape)公司开始研发一种新的语言Mocha,并将其嵌入到自己的浏览器Netscape中,这种语言可以通过操纵DOM(Document ObjectModel,文档对象模型)来修改页面,并加入了对鼠标事件的支持。Mocha使用了C的语法,但是设计思想上主要从函数式语言Scheme那里取得了灵感。当Netscape2发布的时候,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这个名字。
1.1.1动态网页
WEB页面在刚开始的时候,是不能动态修改其内容的,要改变一个页面的内容,需要先对网站上的静态HTML文件进行修改,然后需要刷新浏览器。后来出现的JSP,ASP等服务器端语言可以为页面提供动态的内容,但是如果没有JavaScript则无法在服务器返回之后动态的在前端修改页面,也无法有诸如鼠标移上某页面元素则高亮该元素之类的效果,因此JavaScript的出现大大的丰富了页面的表现,提高了用户体验。
而当AJAX流行起来之后,更多的非常绚丽的WEB应用涌现了,而且呈越来越多的趋势,如Gmail,Google Map,Google Reader,Remember the milk,facebook等等优秀的WEB2.0应用,都大量的使用了JavaScript及其衍生的技术AJAX。
1.1.2浏览器之战
1.1.3标准
1.2 JavaScript语言特性
JavaScript是一门动态的,弱类型,基于原型的脚本语言。在JavaScript中“一切皆对象”,在这一方面,它比其他的OO语言来的更为彻底,即使作为代码本身载体的function,也是对象,数据与代码的界限在JavaScript中已经相当模糊。虽然它被广泛的应用在WEB客户端,但是其应用范围远远未局限于此。下面就这几个特点分别介绍:
1.2.1动态性
动态性是指,在一个Javascript对象中,要为一个属性赋值,我们不必事先创建一个字段,只需要在使用的时候做赋值操作即可,如下例:
- //定义一个对象
- var obj = new Object();
- //动态创建属性name
- obj.name = "an object";
- //动态创建属性sayHi
- obj.sayHi = function(){
- return "Hi";
- }
- obj.sayHi();
//定义一个对象var obj = new Object();//动态创建属性nameobj.name = "an object";//动态创建属性sayHiobj.sayHi = function(){return "Hi";}obj.sayHi();
加入我们使用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());
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());
动态性是非常有用的,这个我们在第三章会详细讲解。
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);
//定义一个变量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);
结果为:
17
6.3
Object
可见,Javascript的变量更像是一个容器,类似与Java语言中的顶层对象Object,它可以是任何类型,解释器会根据上下文自动对其造型。
弱类型的好处在于,一个变量可以很大程度的进行复用,比如String类型的name字段,在被使用后,可以赋值为另一个Number型的对象,而无需重新创建一个新的变量。不过,弱类型也有其不利的一面,比如在开发面向对象的Javascript的时候,没有类型的判断将会是比较麻烦的问题,不过我们可以通过别的途径来解决此问题。
1.2.3解释与编译
通常来说,Javascript是一门解释型的语言,特别是在浏览器中的Javascript,所有的主流浏览器都将Javascript作为一个解释型的脚本来进行解析,然而,这并非定则,在Java版的Javascript解释器rhino中,脚本是可以被编译为Java字节码的。
解释型的语言有一定的好处,即可以随时修改代码,无需编译,刷新页面即可重新解释,可以实时看到程序的结果,但是由于每一次都需要解释,程序的开销较大;而编译型的语言则仅需要编译一次,每次都运行编译过的代码即可,但是又丧失了动态性。
我们将在第九章和第十章对两种方式进行更深入的讨论。
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,Mootools等的涌现,越来越多的绚丽,高体验的WEB应用被开发出来,这些都离不来幕后的JavaScript的支持。
图JavaScript实现的一个WEB幻灯片
浏览器中的JavaScript引擎也进行了长足的发展,比如FireFox3,当时一个宣传的重点就是速度比IE要快,这个速度一方面体现在页面渲染上,另一方面则体现在JavaScript引擎上,而Google的Chrome的JavaScript引擎V8更是将速度发展到了极致。很难想象,如果没有JavaScript,如今的大量的网站和WEB应用会成为什么样子。
我们可以看几个例子,来说明客户端的JavaScript的应用程度:
图 ExtJS实现的一个网络相册,ExtJS是一个非常优秀的JavaScriipt库
图 ExtJS实现的一个表格,具有排序,编辑等功能
当然,客户端的JavaScript各有侧重,jQuery以功能见长,通过选择器,可以完成80%的页面开发工作,并且提供强大的插件机制,下图为jQuery的UI插件:
总之,随着Ajax的复兴,客户端的JavaScript得到了很大的发展,网络上流行着大量的优秀的JavaScript库,现在有一个感性的认识即可,我们在后边的章节会择其尤要者进行详细讲解。
1.3.2服务端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>
<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>
<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/');
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");
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引擎的,因此天生的就具有动态性和可扩展性,因此在开发网络程序上,确实是一个不错的选择。
1.3.3其他应用中的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;"
- });
app.addMenuItem({cName: "-",// menu dividercParent: "View",// append to the View menucExec: "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在其他方面的也得到了广泛的应用。