last update: March 30th 2006
中文版:THIN
最后更新:2006-3-31
看到一個很好的東西在國內沒有被很多人使用起來,實在是不爽,所以花了很大功夫把這個手冊翻譯成中文,由于這篇文章很長,所以,翻譯的工作量很大而且有些地方英文版也沒有說清楚,雖得查看源代碼,好在不是堅持做完了,大家鼓勵下??!^o^
prototype.js是一個非常優雅的javascript基礎類庫,對javascript做了大量的擴展,而且很好的支持Ajax,國外有多個基于此類庫實現的效果庫,也做得很棒。
prototype.js不僅是一個有很大實用價值的js庫,而且有很高的學習價值,所以我強烈建議B/S開發人員和對JS開發感興趣的朋友去瀏覽一些它的源代碼,其中有很多的珠璣,你絕對會覺得讀它的源代碼是一種享受,當然要讀得懂,呵呵。
網上也有人寫過1.3版的源碼解讀,大家可以找來看看。不過1.4版做了很大的擴充,所以希望有朋友寫出1.4版的源碼解讀。
幾點說明:
萬一你沒有使用過大名鼎鼎的prototype.js,那么讓我來告訴你,prototype.js是由Sam Stephenson寫的一個javascript類庫。這個構思奇妙,而且兼容標準的類庫,能幫助你輕松建立有高度互動的web2.0特性的富客戶端頁面。
如果你最近嘗試使用它,你大概了解到文檔并不是作者的一個強項。和在我以前使用這個類庫的不少開發者一樣,一開始,我不得不一頭扎進閱讀prototype.js的源代碼和實驗它的功能中。我想,在我學習完它之后,把我學到的東西分享給大家是件不錯的事。
同時,在本文中,我也將提供一個關于這個類庫提供的objects,classes,functions,extensions這對東東的非官方參考
在閱讀這個文檔時,熟悉Ruby的開發者將會注意到Ruby的一些內建類和本類庫擴展實現之間非常相似。
Advanced javascript guide.
這個類庫帶有很多預定義的對象和實用函數,這些東東的目的顯然是把你從一些重復的打字中解放出來 。
$() 方法是在DOM中使用過于頻繁的 document.getElementById() 方法的一個便利的簡寫,就像這個DOM方法一樣,這個方法返回參數傳入的id的那個元素。
比起DOM中的方法,這個更勝一籌。你可以傳入多個id作為參數然后 $() 返回一個帶有所有要求的元素的一個 Array 對象。
<HTML><HEAD><TITLE> Test Page </TITLE><script src="prototype-1.3.1.js"></script><script>function test1(){var d = $('myDiv');alert(d.innerHTML);}function test2(){var divs = $('myDiv','myOtherDiv');for(i=0; i<divs.length; i++){alert(divs[i].innerHTML);}}</script></HEAD><BODY><div id="myDiv"><p>This is a paragraph</p></div><div id="myOtherDiv"><p>This is another paragraph</p></div><input type="button" value=Test1 onclick="test1();"><br><input type="button" value=Test2 onclick="test2();"><br></BODY></HTML>
另外一個好處是,這個函數能傳入用string表示的對象ID,也可以傳入對象本身,這樣,在建立其它能傳兩種類型的參數的函數時非常有用。
$F()函數是另一個大收歡迎的“快捷鍵”,它能用于返回任何表單輸入控件的值,比如text box,drop-down list。這個方法也能用元素id或元素本身做為參數。
<script>function test3(){alert( $F('userName') );}</script><input type="text" id="userName" value="Joe Doe"><br><input type="button" value=Test3 onclick="test3();"><br>
$A()函數能把它接收到的單個的參數轉換成一個Array對象。
這個方法,結合被本類庫擴展了的Array類,能方便的把任何的可枚舉列表轉換成或拷貝到一個Array對象。一個推薦的用法就是把DOM Node Lists轉換成一個普通的Array對象,從而更有效率的進行遍歷,請看下面的例子。
<script>function showOptions(){var someNodeList = $('lstEmployees').getElementsByTagName('option');var nodes = $A(someNodeList);nodes.each(function(node){alert(node.nodeName + ': ' + node.innerHTML);});}</script><select id="lstEmployees" size="10" ><option value="5">Buchanan, Steven</option><option value="8">Callahan, Laura</option><option value="1">Davolio, Nancy</option></select><input type="button" value="Show the options" onclick="showOptions();" >
$H()函數把一些對象轉換成一個可枚舉的和聯合數組類似的Hash對象。
<script>function testHash(){//let's create the objectvar a = {first: 10,second: 20,third: 30};//now transform it into a hashvar h = $H(a);alert(h.toQueryString()); //displays: first=10&second=20&third=30}</script>
$R()是new ObjectRange(lowBound,upperBound,excludeBounds)的縮寫。
跳到ObjectRange 類文檔可以看到一個關于此類的完整描述. 此時,我們還是先來看一個例子以展示這個縮寫能代替哪些方法吧。其它相關的一些知識可以在Enumerable 對象文檔中找到。
<script>function demoDollar_R(){var range = $R(10, 20, false);range.each(function(value, index){alert(value);});}</script><input type="button" value="Sample Count" onclick="demoDollar_R();" >
Try.these() 方法使得實現當你想調用不同的方法直到其中的一個成功正常的這種需求變得非常容易, 他把一系列的方法作為參數并且按順序的一個一個的執行這些方法直到其中的一個成功執行,返回成功執行的那個方法的返回值。
在下面的例子中, xmlNode.text在一些瀏覽器中好用,但是xmlNode.textContent在另一些瀏覽器中正常工作。 使用Try.these()方法我們可以得到正常工作的那個方法的返回值。
<script>
function getXmlNodeValue(xmlNode){
return Try.these(
function() {return xmlNode.text;},
function() {return xmlNode.textContent;)
);
}
</script>
上面提到的共通方法非常好,但是面對它吧,它們不是最高級的那類東西。它們是嗎?你很可能自己編寫了這些甚至在你的腳本里面有類似功能的方法。但是這些方法只是冰山一角。
我很肯定你對prototype.js感興趣的原因很可能是由于它的AJAX能力。所以讓我們解釋當你需要完成AJAX邏輯的時候,這個包如何讓它更容易。
Ajax 對象是一個預定義對象,由這個包創建,為了封裝和簡化編寫AJAX 功能涉及的狡猾的代碼。 這個對象包含一系列的封裝AJAX邏輯的類。我們來看看其中幾個類。
如果你不使用任何的幫助程序包,你很可能編寫了整個大量的代碼來創建XMLHttpRequest對象并且異步的跟蹤它的進程, 然后解析出響應 然后處理它。當你不需要支持多于一種類型的瀏覽器時你會感到非常的幸運。
為了支持 AJAX 功能。這個包定義了 Ajax.Request 類。
假如你有一個應用程序可以通過url http://yoursever/app/get_sales?empID=1234&year=1998與服務器通信。它返回下面這樣的XML 響應。
<?xml version="1.0" encoding="utf-8" ?><ajax-response><response type="object" id="productDetails"><monthly-sales><employee-sales><employee-id>1234</employee-id><year-month>1998-01</year-month><sales>$8,115.36</sales></employee-sales><employee-sales><employee-id>1234</employee-id><year-month>1998-02</year-month><sales>$11,147.51</sales></employee-sales></monthly-sales></response></ajax-response>
用 Ajax.Request對象和服務器通信并且得到這段XML是非常簡單的。下面的例子演示了它是如何完成的。
<script>function searchSales(){var empID = $F('lstEmployees');var y = $F('lstYears');var url = 'http://yoursever/app/get_sales';var pars = 'empID=' + empID + '&year=' + y;var myAjax = new Ajax.Request(url,{method: 'get',parameters: pars,onComplete: showResponse});}function showResponse(originalRequest){//put returned XML in the textarea$('result').value = originalRequest.responseText;}</script><select id="lstEmployees" size="10" onchange="searchSales()"><option value="5">Buchanan, Steven</option><option value="8">Callahan, Laura</option><option value="1">Davolio, Nancy</option></select><select id="lstYears" size="3" onchange="searchSales()"><option selected="selected" value="1996">1996</option><option value="1997">1997</option><option value="1998">1998</option></select><br><textarea id=result cols=60 rows=10 ></textarea>
你注意到傳入 Ajax.Request構造方法的第二個對象了嗎? 參數{method: 'get', parameters: pars, onComplete: showResponse} 表示一個匿名對象的真實寫法。他表示你傳入的這個對象有一個名為 method 值為 'get'的屬性,另一個屬性名為 parameters 包含HTTP請求的查詢字符串,和一個onComplete 屬性/方法包含函數showResponse。
還有一些其它的屬性可以在這個對象里面定義和設置,如 asynchronous,可以為true 或 false 來決定AJAX對服務器的調用是否是異步的(默認值是 true)。
這個參數定義AJAX調用的選項。在我們的例子中,在第一個參數通過HTTP GET命令請求那個url,傳入了變量 pars包含的查詢字符串, Ajax.Request 對象在它完成接收響應的時候將調用showResponse 方法。
也許你知道, XMLHttpRequest在HTTP請求期間將報告進度情況。這個進度被描述為四個不同階段:Loading, Loaded, Interactive, 或 Complete。你可以使 Ajax.Request 對象在任何階段調用自定義方法 ,Complete 是最常用的一個。想調用自定義的方法只需要簡單的在請求的選項參數中的名為 onXXXXX 屬性/方法中提供自定義的方法對象。 就像我們例子中的 onComplete 。你傳入的方法將會被用一個參數調用,這個參數是 XMLHttpRequest 對象自己。你將會用這個對象去得到返回的數據并且或許檢查包含有在這次調用中的HTTP結果代碼的 status 屬性。
還有另外兩個有用的選項用來處理結果。我們可以在onSuccess 選項處傳入一個方法,當AJAX無誤的執行完后調用, 相反的,也可以在onFailure選項處傳入一個方法,當服務器端出現錯誤時調用。正如onXXXXX 選項傳入的方法一樣,這兩個在被調用的時候也傳入一個帶有AJAX請求的XMLHttpRequest對象。
我們的例子沒有用任何有趣的方式處理這個 XML響應, 我們只是把這段XML放進了一個文本域里面。對這個響應的一個典型的應用很可能就是找到其中的想要的信息,然后更新頁面中的某些元素, 或者甚至可能做某些XSLT轉換而在頁面中產生一些HTML。
在1.4.0版本中,一種新的事件回傳外理被引入。如果你有一段代碼總是要為一個特殊的事件執行,而不管是哪個AJAX調用引發它,那么你可以使用新的Ajax.Responders對象。
假設你想要在一個AJAX調用正在運行時,顯示一些提示效果,像一個不斷轉動的圖標之類的,你可以使用兩個全局事件Handler來做到,其中一個在第一個調用開始時顯示圖標,另一個在最后一個調用完成時隱藏圖標??聪旅娴睦?。
<script>var myGlobalHandlers = {onCreate: function(){Element.show('systemWorking');},onComplete: function() {if(Ajax.activeRequestCount == 0){Element.hide('systemWorking');}}};Ajax.Responders.register(myGlobalHandlers);</script><div id='systemWorking'><img src='spinner.gif'>Loading...</div>
更完全的解釋,請參照 Ajax.Request 參考 和 Ajax選項參考。
如果你的服務器的另一端返回的信息已經是HTML了,那么使用這個程序包中 Ajax.Updater 類將使你的生活變得更加得容易。用它你只需提供哪一個元素需要被AJAX請求返回的HTML填充就可以了,例子比我寫說明的更清楚。
<script>function getHTML(){var url = 'http://yourserver/app/getSomeHTML';var pars = 'someParameter=ABC';var myAjax = new Ajax.Updater('placeholder',url,{method: 'get',parameters: pars});}</script><input type=button value=GetHtml onclick="getHTML()"><div id="placeholder"></div>
你可以看到,這段代碼比前面的例子更加簡潔,不包括 onComplete 方法,但是在構造方法中傳入了一個元素id。 我們來稍稍修改一下代碼來描述如何在客戶端處理服務器段錯誤成為可能。
我們將加入更多的選項, 指定處理錯誤的一個方法。這個是用 onFailure 選項來完成的。我們也指定了一個 placeholder 只有在成功請求之后才會被填充。為了完成這個目的我們修改了第一個參數從一個簡單的元素id到一個帶有兩個屬性的對象, success (一切OK的時候被用到) 和 failure (有地方出問題的時候被用到) 在下面的例子中沒有用到failure屬性,而僅僅在 onFailure 處使用了 reportError 方法。
<script>
function getHTML()
{
var url = 'http://yourserver/app/getSomeHTML';
var pars = 'someParameter=ABC';
var myAjax = new Ajax.Updater({success: 'placeholder'},url,{method: 'get',parameters: pars,onFailure: reportError});}function reportError(request){alert('Sorry. There was an error.');}</script><input type=button value=GetHtml onclick="getHTML()"><div id="placeholder"></div>
如果你的服務器邏輯是連同HTML 標記返回javascript 代碼, Ajax.Updater對象可以執行那段javascript代碼。為了使這個對象對待響應為javascript,你只需在最后參數的對象構造方法中簡單加入evalScripts: true屬性。但是值得提醒的是,像這個選項名evalScripts暗示的,這些腳本會被執行,但是它們不會被加入到Page的腳本中?!坝惺裁磪^別?”,可能你會這樣問。我們假定請求地址返回的東東像這樣:
<script language="javascript" type="text/javascript">function sayHi(){alert('Hi');}</script><input type=button value="Click Me" onclick="sayHi()">
如果你以前這樣嘗試過,你知道這些腳本不會如你所期望的那樣工作,原因是這段腳本會被執行,但像上面這樣的腳本執行并不會創建一個名叫sayHi的函數,它什么也不做。如果要創建一個函數,我們應當把代碼改成下面這個樣子:
<script language="javascript" type="text/javascript">sayHi = function(){alert('Hi');};</script><input type=button value="Click Me" onclick="sayHi()">
為什么我們在上面的代碼中不使用var關鍵字來聲明這個變量呢(指sayHi ),因為那樣做創建出來的函數將只是當前腳本塊的一個局部變量(至少在IE中是這樣)。不寫var關鍵字,創建出來的對象的作用域就是我們所期望的window。
更多相關知識,請參看 Ajax.Updater reference 和options reference.
你知道,我們都是這樣來做循環的,建一個Array,用elements組織它們,再建一個循環結構(例如for,foreach,while)通過index數字來訪問每一個element,再用這個element做一些動作。
當你想到這時,你會發現幾乎每次寫循環代碼你都會遲早用到一個Array。那么,如果Array對象能夠提供更多的功能給它們的迭代器使用不是很爽嗎?確實是這樣,事實上很多的編程語言都在它們的Array或其它類似的結構中(如Collections,Lists)提供一些這樣的功能。
現在好了,prototype.js了給我們一個 Enumerable對象,它實現了很多和可迭代數據進行交互的竅門。和原有的JS對象相比prototype.js更上一層樓,它對Array 類s擴展了所有枚舉要用的函數。
在標準的javascript中,如果你想把一個array中的所有elements顯示出來,你可以像下面代碼這樣寫得很好:
<script>function showList(){var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Meg'];for(i=0;i<simpsons.length;i++){alert(simpsons[i]);}}</script><input type="button" value="Show List" onclick="showList();" >
使用我們新的最好的朋友,prototype.js,我們可以把它生寫成這樣
function showList(){var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Meg'];simpsons.each( function(familyMember){alert(familyMember);});}
你可能會想“非常奇怪的方式...相對舊的,這種語法太怪異了”。哦,在上面的例子,確實什么也沒有,在這個簡單得要死例子中,也沒有改變太多啊,盡管如此,請繼續讀下去。
在繼續下面內容之前,你注意到那個被做為一個參數傳遞給each函數的函數?我們把它理解成迭代器函數。
就如我們上面提到的,把你的Array中的elements當成相同的類型使用相同的屬性和函數是很通用(Common,不知該翻譯成通用還是庸俗)的。讓我們看看怎么樣利用我們新的馬力強勁的Arrays的迭代功能吧。
依照標準找到一個element。
<script>function findEmployeeById(emp_id){var listBox = $('lstEmployees')var options = listBox.getElementsByTagName('option');options = $A(options);var opt = options.find( function(employee){return (employee.value == emp_id);});alert(opt.innerHTML); //displays the employee name}</script><select id="lstEmployees" size="10" ><option value="5">Buchanan, Steven</option><option value="8">Callahan, Laura</option><option value="1">Davolio, Nancy</option></select><input type="button" value="Find Laura" onclick="findEmployeeById(8);" >
現在我們再下一城,看看如何過濾一個Array中的元素,從每個元素中得到我們想要的成員。
<script>function showLocalLinks(paragraph){paragraph = $(paragraph);var links = $A(paragraph.getElementsByTagName('a'));//find links that do not start with 'http'var localLinks = links.findAll( function(link){var start = link.href.substring(0,4);return start !='http';});//now the link textsvar texts = localLinks.pluck('innerHTML');//get them in a single stringvar result = texts.inspect();alert(result);}</script><p id="someText">This <a >text</a> hasa <a href="#localAnchor">lot</a> of<a href="#otherAnchor">links</a>. Some are<a >external</a>and some are <a href="#someAnchor">local</a></p><input type=button value="Find Local Links" onclick="showLocalLinks('someText')">
上面的代碼僅僅是一點小小的實踐讓人愛上這種語法。請參看 Enumerable和Array的所有函數
新聞熱點
疑難解答