AJAX主要有三种:最简单的直接加载整个网页,然后通过innerHTML之类的办法显示在网页上,这个现在用Prototype.js的Ajax.Updater类能轻而易举地实现;另一种最常用的是加载一个只包含数据的文件(比如XML)然后通过一系列代码处理后显示给用户;还有一种是像DWR或PHPRPC那样直接把服务器端的函数生成对应的JS代码给客户端,客户端执行后交由服务器处理,再返回结果。这里我们来讨论一下第二种,基于数据的。

用XML封装数据的实现见得多了,实际上XML不是唯一的办法。对于一些简单的应用,还有一个强大的东西:JSON。简单地说,就是由服务器端生成序列化后的数据(其实就是JS代码,不过和上文提到的第三种AJAX不同的是,服务器端生成的代码是JS由Object和Array多层嵌套组成的变量),由客户端加载之后直接使用JS来解析。乍听之下似乎还是和XML差不多嘛?错了。JSON还有几个XML不能比拟的优势:它是Javascript原生的Object和Array。换句话说,你甚至不用理会XMLHttpRequest。想当初刚尝试以Google Chrome为主要平台写AJAX应用时,才发现Chrome竟然没有自己之前一直在其它浏览器里用的selectSingleNode()!或许习惯不好或方法有误,不过我还是囧。若是使用JSON的话,通过JS原生的方法对Array和Object操作,不会有这个问题。

好了,说了这么多废话,切入正题。关于JSON的实现方式,网上一大堆,这里我只是说说我自己写的一个例子(其实说真的,直到昨天我才知道这叫JSON,否则我早就使用现成的类库而不用自己写代码来解析序列了)

下面是一个典型的JSON格式的数据。它实际上是一个由服务器端程序生成的JS文件,并且还进行了回调的调用:

var data = [
	{
		"name":"张三",
		"age":32
	},
	{
		"name":"李四",
		"age":24
	}
];
_callback("asdfxeg", data);	//回调,其中第一个参数无意,下文会说到

熟悉JS的应该都知道,像“obj={}”这个大括号里面其实是一个Object(对象),我们可以自由地定义它的属性来存放我们想要的数据;而中括号[]则是一个Array(数组),Array可以包含任意类型的数据,当然也包括Object,这也就是JSON的基本结构了。

好了,说完数据结构,下面来说一下怎么加载它。首先,既然是AJAX,自然可以用我们平常见到的XMLHttpRequest,然后把拉下来的数据用window.eval()方法执行一下,便加载下来了。JSON的魅力并不仅仅于此,之前已经说了,它实际上是100%原生的JS,你可能也想到了,那就是直接用DOM加载!

下面是一个使用DOM加载JS的实例:

var js = document.createElement("script");	//创建一个DOM对象
js.type = "text/javascript";	//定义对象类型为text/javascript
js.src = "data.js";	//定义script来源为data.js的数据
document.body.appendChild(js);	//把这个script对象追加到网页中,同时它的代码(src中指定的文件)会开始执行

多么美妙的方法,少了烦人的代码去创建XHR对象,并且还能跨域调用,帅呆了!

且慢,别忙着拷贝上面的代码。这段代码只是演示了怎么使用JS来加载JS。事实上我自己写了一系列函数来管理这些加载动作,包含了对回调的处理等等。下面是代码:

var callback_pool = new Array();	//管理回调函数的数组,我称之为“回调池”
function _callback(id, data)	//数据加载完成后首先执行的处理函数,用于管理回调
{
	//每一次调用请求都有它唯一的id,用于对回调进行识别
	if (typeof(callback_pool[id]) != "undefined")	//检查回调池中是否有该id的回调函数
	{
		if (callback_pool[id] != null)
		{
			callback_pool[id](data);	//执行回调并把数据传递给它。
			callback_pool[id] = null;	//调用请求完成,从回调池中清除该回调函数。
		}
	}
	if (document.getElementById(id))
	{
		document.body.removeChild(document.getElementById(id));
		//从网页中移除包含这个请求的JSON数据的script,因为我们已经把数据传递给相关的处理函数,它已经完成使命了。
	}
}
function loadJS(id, params)	//发起请求
{
	var js = document.createElement("script");	//创建一个script对象
	js.id = id;	//设置该script对象id
	js.type = "text/javascript";	//定义对象类型为text/javascript
	js.src = "data.php?id=" + id + "&" + params;
	document.body.appendChild(js);	//把这个script对象追加到网页中
}
function randomId()	//生成唯一ID
{
	var key = "0123456789qwertyuioplkjhgfdsazxcvbnm";
	var tmp = "script_";
	for (var i = 0; i < 5; i++)
	{
		tmp += key.charAt(Math.ceil(Math.random() * 100000000) % key.length);
	}
	return  tmp;
}

说一下这个系统的流程:首先为这个请求声明一个唯一ID和回调处理函数,把回调处理函数以ID为键名加入到回调池中储存,调用loadJS发起请求并把ID和相应参数传递给后台处理程序,后台处理程序返回ID和处理结果并触发_callback函数,_callback函数通过ID在回调池中找到这个请求的回调处理,并把数据传递给它,最后把加载的数据清理掉,完成请求过程。

下面是负责后台处理数据的PHP程序。为了简洁,这里我省略了数据查询部分,仅仅是输出了一个JSON序列

<?php
header("Content-Type:text/javascript");
echo "var data = [/n";
echo "/t{/n";
echo "/t/t/"name/":/"张三/",/n";
echo "/t/t/"age/":32/n";
echo "/t},/n";
echo "/t{/n";
echo "/t/t/"name/":/"李四/",/n";
echo "/t/t/"age/":24/n";
echo "/t}/n";
echo "];/n";
echo "_callback(/"{$_GET['id']}/", data);";
?>

有人建议不需要诸如/t(Tab符)、/n(换行符)这类符号(为了节省数据量),这是对的,但是这里为了学习所以对输出的数据格式化了一下。要知道,当数据多起来的时候没有换行符你(也可能连同你的文本编辑器:-) 如果你用Notepad++的话)会崩溃的。。。

好吧,说了这么多,还没说怎么调用数据呢

var sid = randomId();	//生成随机ID
var params = "act=getusers";	//查询参数
callback_pool[sid] = function(data)	//回调处理
{
	//干点什么吧,比如说弹出数组中第一个元素的name
	alert(data[0].name);
}
loadJS(id, params);	//发起请求

其实到这里,这篇文章已经写完了,我想表达的我都尽量说了,刚开始写技术文章,还有点不知怎么办