您所在的位置: 首页>>读书频道>>设计开发>>.Net系列>>

Prototype

http://book.51cto.com  2008-04-29 23:55  李秀忠  电子工业出版社  我要评论(0)
  • 摘要:《Adding Ajax中文版》是通过添加Ajax效果,来增强现有web应用程序的功能。本文介绍了Prototype。
  • 标签:Ajax  .Net

Prototype

在这些Ajax库当中,最有名的,而且也是用得最多的,可能就是Prototype了。实际上,它是一个相当小的,而且完全独立的库。它加载迅速,并且是构成其他库的核心,包括我们将在后面所讨论的script.aculo.us和Rico。

Prototype是一个比脚本语言equalizer还要小的框架,它提供了一套对象,从而大大降低了在不同的浏览器之间访问页面元素,以及和这些页面元素进行协作的复杂度。它是由Sam Stephenson创建的,而且仍旧由他负责维护,你可以在http://prototypejs.org网站上下载到它的最新版本(在写本书时,它的版本是Prototype 1.5)。网站上还有这个库的API文档,以及如何使用它的相关文章和指南。

把Prototype添加到项目中是非常简单的。使用如下代码,就可以把它包含到一个应用程序中:

<script type="text/javascript" src="/path/to/prototype.js"></script>

Prototype的一个鲜明特征是:它整合了几个和Ruby语言类似的函数和行为。Ruby是一门简单易用的语言,但是,它也是比较晦涩的,尤其是,如果你不熟悉Prototype的一些独特组件,那么在JavaScript中,Prototype对这些函数的应用就非常容易混淆。在这些组件当中最关键的就是众所周知的dollar和dollar F函数。

dollar函数,$(),根据传递来的元素名,返回这个元素的引用:

var elem = $('some_element');

使用id属性定义元素:

<div id="some_element">
...
</div>

它和使用如下的DOM方法调用相当:

var elem = document.getElementById('some_element');

除了返回元素之外,$()函数还附加了几个Prototype特有的属性。

$F()函数根据给定的标识符返回表单域的值:

var value = $F('form_field');
...
<input type="text" id="form_field" />

它相当于:

var value = document.getElementById('form_field').value

为了更方便地访问一些单元,Prototype重新封装了一些通用功能,而它所提供的新功能还没有这些通用功能多。使用document.getElementById非常简单,但是在增加了功能之后,使用$()更为简单。

在Prototype中,另外一个关键的组件是Enumerable对象,它在JavaScript中提供了迭代的能力。至少相对于其他语言来说,在JavaScript中遍历数据集合,例如:数组,始终是比较原始的。在新版本的JavaScript中,这种情况也正在改变,例如,在JavaScript 1.6中加入了forEach和filter功能,而在JavaScript 1.7中加入了迭代功能。只有这些变更在所有浏览器的供应商中全面普及,Prototype的Enumerable对象,才能够像那些内置的功能一样被应用。

Prototype也提供了一些用来管理XMLHttpRequest的对象。它们的使用并不复杂,而且它们简化了建立Web服务请求的代码。你完全可以适当地调用基本的Ajax对象——Ajax,但是大多数工作都是通过Ajax.Request这个对象来完成的。如下就是使用这个对象建立请求的示例:

var myAjax = new Ajax.Request( url, {
method: 'get',
parameters: qry,
onComplete: printRecipe });

第一个参数是这个Web服务请求的URL;第二个参数接受一个对象,这个对象拥有一个HTTP请求方法、一些服务请求参数、以及当这个请求完成时需要调用的函数。

XMLHttpRequest响应会被封装在Prototype中,你不能拦截不同阶段的请求。相反,应该操纵onComplete、onLoading、onLoaded以及onInteractive事件来处理各个阶段。对于大多数应用程序,你可能只对onComplete,或者表示请求成功的onSuccess感兴趣。

通过使用第2章所讨论的可选POST方式,Prototype还提供了一些方法来模拟HTTP请求,DELETE和PUT。这个库还提供了自动估计返回数据的格式,如果这些数据是HTML格式,Ajax.Updater方法可以处理这个响应,并且把这些数据插入到指定元素中;如果这些数据被标记为JSON,Prototype也会自动处理它们,并且把它们作为对象传递给回调函数。在这里关键的短语是“被标记为JSON”,也就是说,要让Protatype自动返回数据需要提供X-JSON头。Ruby on Rails,这个Prototype的事实服务端环境就支持这样做。遗憾的是,使用定制的头可能会失败,尤其是在处理返回数据的大小方面。由于不想使用非标准的定制头,本章乃至本书中的示例,直接在响应对象中返回JSON格式的数据,并且直接使用eval或者json.org的JSON 赋值器。

要介绍这些对Ajax、枚举对象以及dollar函数的研究,最好的方式就是示例。示例3-1演示了通过Ajax对象,如何使用Prototype来建立Web服务请求。这个Web服务和第2章示例2-9中所使用的服务类似,只是不再发送一连串的搜索条件,该Web服务以JSON格式返回所有饮料配方。

示例3-1:以JSON格式返回饮料配方列表的PHP应用程序

<?php

  $result = "'drinks' :  [{ 'drink' : 'TEA', 'makings' : { 'title' : 'Hot Tea'," .
" 'ingredients' : [ {  'ingredient' : 'tea leaves' }]," .
"  'instruction' : 'Boil water. Pour over tea leaves." .
" Steep five minutes. Strain and serve.'}}";
$result .= ", { 'drink' : 'NONCHAMP', 'makings' : {'title' : 'Non-Alcoholic Champagne',
" .
" 'ingredients' : [ { 'ingredient' : '32 ounces club soda' }, " .
" { 'ingredient' : '12 ounces frozen white grape juice concentrate'}],"
.
" 'instruction' : 'Mix club soda with grape juice concentrate.'}}";
$result .= ", { 'drink' : 'APPLETINI', 'makings' : { 'title' : 'Appletini', " .
" 'ingredients' : [ { 'ingredient' : '1 ounce vodka' }, " .
" { 'ingredient' : '1/2 ounce Sour Apple Pucker or apple schnapps'}],"
.
" 'instruction' : 'Mix vodka and schnapps in a glass filled with ice.
Strain " .
"into martini glass. Garnish with an apple slice or raisin.'}}";
$result .= ", { 'drink' : 'SWMPMARGARITA', 'makings' : {'title' : 'Swamp Margaria', " .
" 'ingredients' : [ { 'ingredient' : '1 1/2 ounce good quality
tequila'}, " .
" { 'ingredient' : '3/4 ounce Cointreau'}, " .
" { 'ingredient' : '3/4 ounce Grand Marnier'}, " .
" { 'ingredient' : '1/2 ounce lime juice'}, " .
" { 'ingredient' : '2 ounces sour mix'}, " .
" { 'ingredient' : 'several green olives'}]," .
" 'instruction' : 'Mix all ingredients. Chill an hour. " .
"Fill bottom of tall glass with several green olives. " .
"Poor margarita over the olives, let sit for ten minutes, strain and
serve.'}}";
$result .= ", { 'drink' : 'LEMON', 'makings' : {'title' : 'Lemon Drop', " .
" 'ingredients' : [ { 'ingredient' : '1 ounce lemon vodka'}, " .
" { 'ingredient' : '1 ounce lemon juice'}, " .
" { 'ingredient' : '1 teaspoon sugar'}]," .
" 'instruction' : 'Shake with ice, " .
"strain and serve.'}}] ";
echo $result;

?>

当返回服务请求时,我们借助eval函数来处理JSON格式的数据,将其格式化为带有一组drink对象的JavaScript对象。这里使用饮料名称来标识每个drink对象,而每个drink对象又包含更深层的对象makings,makings对象包含饮料标题、成分和指导等信息。

正如示例3-2所示,在Ajax应用程序的客户端部分,当从下拉列表中选择一种饮料时,就会通过Web服务URL建立一个Prototype的Ajax.Request对象。当然,这个请求还会接收到其他信息,包括当完成一个成功的请求时,会调用哪个函数。当这些参数都准备好后,它们会被指定给这个对象的parameters属性。在回调函数中, 我们将借助eval函数调用,根据响应文本建立一个包含所有饮料配方的配方对象,并且调用printRecipe函数来处理这些结果。

在printRecipe中,$F()函数用于获取饮料名称,而$()函数用于获取包含已打印配方的元素。

在这个应用程序中,下一个我们感兴趣的部分是什么呢?那就是:从列表中找回选中的饮料。我们将使用Prototype的Enumerable对象中的find方法,传递一个执行比较功能的匿名函数,在这里,我们将比较饮料名称。如果找到匹配项时,它会返回一个特定的drink对象,这个对象会带有被选择饮料的所有信息。

drink对象还有一个属性是makings,它包含饮料的标题、成分和指导。直接访问该项目就可以得到标题和指导,但是,成分属性是一个包含单个成分的数组,要处理它,需要使用另外两个Prototype的Enumerable对象:先是collect,然后是join。所有的这些内容都会被连成一个字符串,用于更新配方对象的现有内容。示例3-2显示了这个应用程序的JavaScript代码。

示例3-2:使用Prototype的Enumerable对象,在drink对象内进行搜索

var recipeObj;

Event.observe(window,'load',function() {
Event.observe('myform','submit',getRecipe);
});

function getRecipe(evnt) {
Event.stop(evnt);
if (recipeObj) {
printRecipe();
return;
}
var url = 'query1.php';
var myAjax = new Ajax.Request( url,
{
method:'get',
 
示例3-2:使用Prototype的Enumerable对象,在drink对象内进行搜索(续例)

onSuccess: function(transport){
eval("recipeObj = {" + transport.responseText + "}");
printRecipe();
},
onFailure: function(){ alert('Something went wrong...') }
});

}
function printRecipe() {

      // 获得饮料名称和配方空间对象
var drinkName = $F('drink');

      // 搜索饮料名称
var drinkObj = recipeObj.drinks.find(function(drink) {
if (drink.drink == drinkName) {
return drink;
}
});

      if (!drinkObj) return;

      var recipe = document.createElement('div');
Element.extend(recipe);
recipe.id = 'recipe';
recipe.addClassName('recipe');

      var str = "<h3>" + drinkObj.makings.title + "</h3>";
var ingredients = drinkObj.makings.ingredients.collect(function(ingredient) {
return ingredient.ingredient;
});
str += ingredients.join('<br />');
str += "<br /><br />" + drinkObj.makings.instruction;
if ($('recipe'))
$('recipe').remove('recipe');
var recipe = document.createElement('div');
Element.extend(recipe);
recipe.id = 'recipe';
recipe.addClassName('recipe');
recipe = recipe.update(str);
document.body.appendChild(recipe);
}

在getRecipe函数中,如果已经示例化了recipeObj,那么就会调用printRecipe函数;否则,会建立一个Ajax请求,并且在processRecipe函数中建立recipeObj对象。这样可以阻止对Web服务的多次调用。

除非是首次请求,服务器上会有所延迟,否则,当选择了饮料名称时,配方信息几乎会立即被打印出来。

这个Web页面本身和第2章示例2-10没有什么两样,只是它使用了Prototype库。这个应用程序专用的JavaScript位于一个叫做getalldrinks.js的文件中。本书的示例文件将通过ch03-02.xhtml来引用它。

我们还不能肯定Prototype是否充分利用了“JavaScript的土壤”发挥了它的作用,但是Enumerable对象确实提供了非常有吸引力的功能。那么,Enumerable对象是如何工作的呢?毕竟,它不同于建立其他特殊对象。就目前我们所了解的知识,我们只是直接处理JavaScript对象。

Prototype中的Enumerable,以及其他很多对象都是通过JavaScript的prototype属性,我推测是通过名称来工作的。如果你对prototype的使用还不是很清楚,你可以在下一节所涉及的概述中,得到一些基础知识。在Ajax中,继承对象的prototype这种用法是非常普遍的,然而,普遍不一定就没有争议。如果你对prototype属性比较熟悉,你就可以轻松地跳过这一节。


回书目   上一节   下一节
专题:ASP.NET 2.0基础开发指南
.NET移动与嵌入式技术专题
.NET Framework新手入门专题
VS.NET实用开发专题
ADO.NET实用技巧专题
 
 验证码: (点击刷新验证码)   匿名发表
  • Visual C++ 完全自学宝典

  • 作者:强锋科技,朱洪波
  • Visual C++ 6.0是微软公司为程序人员提供的Visual Studio 6.0工具套件中的重要组成部分。本书由浅入深地介绍使用Visual C++ 6.0..
Copyright©2005-2008 51CTO.COM 版权所有