前言
從2006年第一次接觸JavaScript至今,算來也有四年時間了。上大學(xué)的時候,有一段時間沉迷于函數(shù)式編程,而那時候?qū)avaScript的理解僅僅局限在這是用來做網(wǎng)頁的一種腳本,那些很惡心的彈出窗口和不斷在頁面上漂浮的黃色廣告就是用JavaScript做出來的。工作之后,由于我們產(chǎn)品本身就是基于WEB展現(xiàn)的,而這個時候Ajax已經(jīng)復(fù)興數(shù)年了,但是于我,則只有“Ajax可以實現(xiàn)頁面的局部刷新”這樣一個概念。在實際的應(yīng)用中,我才有機會面對真正的JavaScript,早期關(guān)于函數(shù)式編程的概念開始在我的記憶力復(fù)蘇,而真正將JavaScript比較熟練的應(yīng)用在產(chǎn)品的開發(fā)中,那已經(jīng)是09年年末了。
開始研究JavaScript,是從2008年的5月左右,我從當(dāng)當(dāng)上買了一本犀牛書,并下載到一本蝴蝶書,結(jié)合著實際工作的的要求,一步一步的開始了JavaScript的探索。剛開始也只是被陷入在DOM的泥潭中,慢慢的開始考慮更進一步的東西,比如JSON作為數(shù)據(jù)交換,window對象的一些特殊的方法如eval等等,事實上仍然是陷入在語法的細節(jié)中。當(dāng)終于有一天,理解了變量的作用域,理解了閉包,理解了函數(shù)的一等性,以往用來類比的來源于其他語言的所有概念則完全被顛覆了,當(dāng)理解了JavaScript中的這些基本概念(雖然難以理解,但是它們的確是基本概念)之后,在回顧來時的路,則有些一覽眾山小的意思。
但是,這些看似基本的概念,在初學(xué)時如果混入了其他語言的概念以進行類比,則后患無窮,所以,學(xué)習(xí)JavaScript其實就一句話:“將JavaScript當(dāng)成一門新的語言來學(xué)習(xí)”。鑒于我學(xué)習(xí)JavaScript的歷程中,遇到了很多問題,但是最終都一一克服了,在這個過程中,有些心得,有些體會,就分節(jié)貼出來,做成一個系列,以方便初學(xué)JavaScript的朋友們。
在20世紀(jì)90年代,也就是早期的WEB站點上,所有的網(wǎng)頁內(nèi)容都是靜態(tài)的,所謂靜態(tài)是指,除了點擊超鏈接,你無法通過任何方式同頁面進行交互,比如讓頁面元素接受事件,修改字體等。人們于是迫切的需要一種方式來打破這個局限,于是到了1996年,網(wǎng)景(Netscape)公司開始研發(fā)一種新的語言Mocha,并將其嵌入到自己的瀏覽器Netscape中,這種語言可以通過操縱DOM(Document Object Model,文檔對象模型)來修改頁面,并加入了對鼠標(biāo)事件的支持。Mocha使用了C的語法,但是設(shè)計思想上主要從函數(shù)式語言Scheme那里取得了靈感。當(dāng)Netscape 2發(fā)布的時候,Mocha被改名為LiveScript,當(dāng)時可能是想讓LiveScript為WEB頁面注入更多的活力。后來,考慮到這個腳本語言的推廣,網(wǎng)景采取了一種宣傳策略,將LiveScript更名為JavaScript,目的是為了跟當(dāng)時非常流行的面向?qū)ο笳Z言Java發(fā)生曖昧的關(guān)系。這種策略顯然頗具成效,以至于到現(xiàn)在很多初學(xué)者還會為JavaScript和Java的關(guān)系而感到困惑。
Javascript取得成功了之后,確實為頁面注入了活力,微軟也緊接著開發(fā)自己的瀏覽器腳本語言,一個是基于BASIC語言的VBScript,另一個是跟Javascript非常類似的Jscript,但是由于Javascript已經(jīng)深入人心,所以在隨后的版本中,微軟的IE幾乎是將Javascript作為一個標(biāo)準(zhǔn)來實現(xiàn)。當(dāng)然,兩者仍然有不兼容的地方。1996年后期,網(wǎng)景向歐洲電腦廠商協(xié)會(ECMA)提交了Javascript的設(shè)計,以申請標(biāo)準(zhǔn) <script src="/javascripts/tinymce/themes/advanced/langs/zh.js" type="text/javascript"></script><script src="/javascripts/tinymce/plugins/javaeye/langs/zh.js" type="text/javascript"></script> 化,ECMA去掉了其中的一些實現(xiàn),并提出了ECMAScript-262標(biāo)準(zhǔn),并確定Javascript的正式名字為ECMAScript,但是JavaScript的名字已經(jīng)深入人心,故本書中仍沿用Javascript這個名字。
WEB頁面在剛開始的時候,是不能動態(tài)修改其內(nèi)容的,要改變一個頁面的內(nèi)容,需要先對網(wǎng)站上的靜態(tài)HTML文件進行修改,然后需要刷新瀏覽器。后來出現(xiàn)的JSP,ASP等服務(wù)器端語言可以為頁面提供動態(tài)的內(nèi)容,但是如果沒有JavaScript則無法在服務(wù)器返回之后動態(tài)的在前端修改頁面,也無法有諸如鼠標(biāo)移上某頁面元素則高亮該元素之類的效果,因此JavaScript的出現(xiàn)大大的豐富了頁面的表現(xiàn),提高了用戶體驗。
而當(dāng)AJAX流行起來之后,更多的非常絢麗的WEB應(yīng)用涌現(xiàn)了,而且呈越來越多的趨勢,如Gmail,Google Map,Google Reader,Remember the milk,facebook等等優(yōu)秀的WEB2.0應(yīng)用,都大量的使用了JavaScript及其衍生的技術(shù)AJAX。
JavaScript是一門動態(tài)的,弱類型,基于原型的腳本語言。在JavaScript中“一切皆對象”,在這一方面,它比其他的OO語言來的更為徹底,即使作為代碼本身載體的function,也是對象,數(shù)據(jù)與代碼的界限在JavaScript中已經(jīng)相當(dāng)模糊。雖然它被廣泛的應(yīng)用在WEB客戶端,但是其應(yīng)用范圍遠遠未局限于此。下面就這幾個特點分別介紹:
動態(tài)性是指,在一個Javascript對象中,要為一個屬性賦值,我們不必事先創(chuàng)建一個字段,只需要在使用的時候做賦值操作即可,如下例:
//定義一個對象var obj = new Object();//動態(tài)創(chuàng)建屬性nameobj.name = "an object";//動態(tài)創(chuàng)建屬性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());
動態(tài)性是非常有用的,這個我們在第三章會詳細講解。
與Java,C/C++不同,Javascript是弱類型的,它的數(shù)據(jù)類型無需在聲明時指定,解釋器會根據(jù)上下文對變量進行實例化,比如:
//定義一個變量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);
結(jié)果為:
可見,Javascript的變量更像是一個容器,類似與Java語言中的頂層對象Object,它可以是任何類型,解釋器會根據(jù)上下文自動對其造型。
弱類型的好處在于,一個變量可以很大程度的進行復(fù)用,比如String類型的name字段,在被使用后,可以賦值為另一個Number型的對象,而無需重新創(chuàng)建一個新的變量。不過,弱類型也有其不利的一面,比如在開發(fā)面向?qū)ο蟮腏avascript的時候,沒有類型的判斷將會是比較麻煩的問題,不過我們可以通過別的途徑來解決此問題。
通常來說,Javascript是一門解釋型的語言,特別是在瀏覽器中的Javascript,所有的主流瀏覽器都將Javascript作為一個解釋型的腳本來進行解析,然而,這并非定則,在Java版的Javascript解釋器rhino中,腳本是可以被編譯為Java字節(jié)碼的。
解釋型的語言有一定的好處,即可以隨時修改代碼,無需編譯,刷新頁面即可重新解釋,可以實時看到程序的結(jié)果,但是由于每一次都需要解釋,程序的開銷較大;而編譯型的語言則僅需要編譯一次,每次都運行編譯過的代碼即可,但是又喪失了動態(tài)性。
我們將在第九章和第十章對兩種方式進行更深入的討論。
當(dāng)Javascript第一次出現(xiàn)的時候,是為了給頁面帶來更多的動態(tài),使得用戶可以與頁面進行交互為目的的,雖然Javascript在WEB客戶端取得了很大的成功,但是ECMA標(biāo)準(zhǔn)并沒有局限其應(yīng)用范圍。事實上,現(xiàn)在的Javascript大多運行與客戶端,但是仍有部分運行于服務(wù)器端,如Servlet,ASP等,當(dāng)然,Javascript作為一個獨立的語言,同樣可以運行在其他的應(yīng)用程序中,比如Java版的JavaScript引擎Rhino,C語言版的SpiderMonkey等,使用這些引擎,可以將JavaScript應(yīng)用在任何應(yīng)用之中。
客戶端的JavaScript隨著AJAX技術(shù)的復(fù)興,越來越凸顯了Javascript的特點,也有越來越多的開發(fā)人員開始進行JavaScript的學(xué)習(xí),使用Javascript,你可以使你的WEB頁面更加生動,通過AJAX,無刷新的更新頁面內(nèi)容,可以大大的提高用戶體驗,隨著大量的JavaScript包如jQuery, ExtJS,Mootools等的涌現(xiàn),越來越多的絢麗,高體驗的WEB應(yīng)用被開發(fā)出來,這些都離不來幕后的JavaScript的支持。
圖JavaScript實現(xiàn)的一個WEB幻燈片
瀏覽器中的JavaScript引擎也進行了長足的發(fā)展,比如FireFox 3,當(dāng)時一個宣傳的重點就是速度比IE要快,這個速度一方面體現(xiàn)在頁面渲染上,另一方面則體現(xiàn)在JavaScript引擎上,而Google的Chrome的JavaScript引擎V8更是將速度發(fā)展到了極致。很難想象,如果沒有JavaScript,如今的大量的網(wǎng)站和WEB應(yīng)用會成為什么樣子。
我們可以看幾個例子,來說明客戶端的JavaScript的應(yīng)用程度:
圖 ExtJS實現(xiàn)的一個網(wǎng)絡(luò)相冊,ExtJS是一個非常優(yōu)秀的JavaScriipt庫
圖 ExtJS實現(xiàn)的一個表格,具有排序,編輯等功能
當(dāng)然,客戶端的JavaScript各有側(cè)重,jQuery以功能見長,通過選擇器,可以完成80%的頁面開發(fā)工作,并且提供強大的插件機制,下圖為jQuery的UI插件:
總之,隨著Ajax的復(fù)興,客戶端的JavaScript得到了很大的發(fā)展,網(wǎng)絡(luò)上流行著大量的優(yōu)秀的JavaScript庫,現(xiàn)在有一個感性的認識即可,我們在后邊的章節(jié)會擇其尤要者進行詳細講解。
相對客戶端而言,服務(wù)器端的JavaScript相對平淡很多,但是隨著JavaScript被更多人重視,JavaScript在服務(wù)器端也開始迅速的發(fā)展起來,Helma, Apache Sling等等。在服務(wù)器端的JavaScript比客戶端少了許多限制,如本地文件的訪問,網(wǎng)絡(luò),數(shù)據(jù)庫等。
一個比較有意思的服務(wù)端JavaScript的例子是Aptana的Jaxer,Jaxer是一個服務(wù)器端的Ajax框架,我們可以看這樣一個例子(例子來源于jQuery的設(shè)計與實現(xiàn)這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屬性說明腳本運行在客戶端還是服務(wù)器端,client表示運行在客戶端,server表示運行在服務(wù)器端,而both表示可以運行在客戶端和服務(wù)器端,這個腳本可以訪問文件,并將文件加載到一個textarea的DOM元素中,還可以將textarea的內(nèi)容通過Form表單提交給服務(wù)器并保存。
再來看另一個例子,通過Jaxer對數(shù)據(jù)庫進行訪問:
<script runat="server">var rs = Jaxer.DB.execute("SELECT * FROM table");var field = rs.rows[0].field;</script>
通過動態(tài),靈活的語法,再加上對原生的資源(如數(shù)據(jù)庫,文件,網(wǎng)絡(luò)等)操作的支持,服務(wù)器端的JavaScript應(yīng)用將會越來越廣泛。
當(dāng)Google的JavaScript引擎V8出現(xiàn)以后,有很多基于V8引擎的應(yīng)用也出現(xiàn)了,其中最著名,最有前景的當(dāng)算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
程序?qū)诳刂婆_上打?。?
Server running at http://127.0.0.1:8000/
訪問http://127.0.0.1:8000,兩秒鐘之后頁面會響應(yīng):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,當(dāng)客戶端連接到來時,程序在控制臺上打印
hello
當(dāng)接收到新的數(shù)據(jù)時,會將接收到的數(shù)據(jù)原樣返回給客戶端,如果客戶端斷開連接,則向控制臺打?。?
goodbay
Node提供了豐富的API來簡化服務(wù)器端的網(wǎng)絡(luò)編程,由于Node是基于一個JavaScript引擎的,因此天生的就具有動態(tài)性和可擴展性,因此在開發(fā)網(wǎng)絡(luò)程序上,確實是一個不錯的選擇。
通過使用JavaScript的引擎的獨立實現(xiàn),比如Rhino,SpliderMonkey,V8等,可以將JavaScript應(yīng)用到幾乎所有的領(lǐng)域,比如應(yīng)用程序的插件機制,高級的配置文件分析,用戶可定制功能的應(yīng)用,以及一些類似與瀏覽器場景的比如Mozilla的ThunderBrid,Mozilla的UI框架XUL,筆者開發(fā)的一個Todo管理器sTodo(在第十章詳細討論)等。
圖 sTodo 一個使用JavaScript來提供插件機制的Java桌面應(yīng)用
Java版的JavaScript引擎原生的可以通過使用Java對象,那樣將會大大提高JavaScript的應(yīng)用范圍,如數(shù)據(jù)庫操作,服務(wù)器內(nèi)部數(shù)據(jù)處理等。當(dāng)然,JavaScript這種動態(tài)語言,在UI方面的應(yīng)用最為廣泛。
著名的Adobe reader也支持JavaScript擴展,并提供JavaScript的API來訪問PDF文檔內(nèi)容,可以通過JavaScript來定制Adobe Reader的界面以及功能等。
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實例為一個在線的遺傳算法的演示,給定一個圖片,然后將一些多邊形(各種顏色)拼成一個圖片,拼圖的規(guī)則為使用遺傳算法,使得這些多變形組成的圖片與目標(biāo)圖片最為相似:
可見,JavaScript在其他方面的也得到了廣泛的應(yīng)用。
附:由于作者本身水平有限,文中難免有紕漏錯誤等,或者語言本身有不妥當(dāng)之處,歡迎及時指正,提出建議,參與討論,謝謝大家!