在原生 JavaScript 中实现 AJAX 异步请求,需要面对 XHR 对象创建、浏览器兼容(如 IE6 的 ActiveXObject)、请求状态监听、响应数据解析、错误处理等一系列繁琐操作,代码冗长且易出错;而 Prototype.js 封装的 Ajax.Request 核心方法,以及 Ajax.Responders 全局响应器,将 AJAX 请求的全流程抽象为简洁、可配置的接口 ------ 从请求发起、参数传递,到回调处理、响应解析,再到全局请求管控,都实现了 "标准化、低心智负担" 的封装,让开发者无需关注底层 XHR 实现,只需聚焦业务逻辑,成为早期前端异步开发的 "标配工具"。

一、原生 AJAX 的痛点
要理解 Ajax.Request 的价值,首先需厘清原生 AJAX 开发的核心痛点。原生实现一个完整的 AJAX 请求,需经历 6 个核心步骤,且每个步骤都隐含兼容和易错点:
1. XHR 对象创建的兼容问题
javascript
// 原生创建 XHR:需兼容 IE6/7 的 ActiveXObject
function createXHR() {
let xhr;
try {
// 标准浏览器
xhr = new XMLHttpRequest();
} catch (e) {
// IE6/7
try {
xhr = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
}
return xhr;
}
2. 请求配置与参数处理的繁琐
- 需手动设置请求方法、URL、是否异步;
- 参数需手动拼接为
key=value&key2=value2格式,且需编码(encodeURIComponent); - 请求头需手动调用
setRequestHeader设置,且需区分 POST/GET 方式的参数传递(GET 拼 URL,POST 放 send 中)。
3. 状态监听与回调拆分的复杂
需监听 onreadystatechange 事件,手动判断 readyState === 4(请求完成),再区分状态码(status >=200 && status <300 为成功),回调逻辑混杂在一个函数中,难以维护:
javascript
const xhr = createXHR();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >=200 && xhr.status <300) {
// 成功回调
const data = JSON.parse(xhr.responseText);
console.log('成功', data);
} else {
// 失败回调
console.log('失败', xhr.status);
}
}
};
xhr.open('POST', '/api/messages', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
const params = `content=${encodeURIComponent('测试留言')}`;
xhr.send(params);
4. 响应数据解析的重复工作
需手动判断响应类型(JSON/XML/ 文本),手动调用 JSON.parse(还需处理解析错误),原生 XHR 无统一的响应包装对象。
5. 全局请求管控的缺失
若需对所有 AJAX 请求添加 "加载中" 提示、统一错误处理(如 401 未登录),需在每个请求中重复编写逻辑,无法批量管控。
以上痛点叠加,导致原生 AJAX 开发效率低、代码冗余、维护成本高。而 Ajax.Request 正是为解决这些问题而生,它将所有繁琐逻辑封装在内部,提供统一、简洁的配置化接口。
二、AJAX 请求基础
Ajax.Request 是 Prototype.js 处理异步请求的核心方法,其设计理念是 "配置化驱动请求,标准化处理响应 "------ 通过一个配置对象(options)定义请求的所有规则,通过标准化的回调函数和响应对象处理结果,彻底摆脱原生 XHR 的繁琐操作。
1. 语法结构与核心配置
基础语法
javascript
new Ajax.Request(url, options);
url:字符串,请求的目标 URL(必填);options:对象,请求配置项(可选),涵盖请求方式、参数、头信息、回调等所有规则。
核心配置项详解
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
method |
字符串 | POST |
请求方式,支持 GET/POST/PUT/DELETE 等 HTTP 方法 |
parameters |
对象 / 字符串 | null |
请求参数,对象会自动序列化为 key=value 格式,字符串需手动编码 |
requestHeaders |
对象 | {} |
请求头,如 { 'Content-Type': 'application/json', 'Token': 'xxx' } |
asynchronous |
布尔值 | true |
是否异步请求,false 为同步(不推荐,会阻塞页面) |
contentType |
字符串 | 自动判断 | 请求体类型,默认 application/x-www-form-urlencoded,JSON 请求需手动设置 |
encoding |
字符串 | UTF-8 |
请求参数的编码格式 |
timeout |
数字 | 0 |
超时时间(毫秒),0 表示无超时 |
onCreate |
函数 | null |
请求创建时触发(XHR 对象初始化后) |
onComplete |
函数 | null |
请求完成时触发(无论成功 / 失败 / 异常) |
onException |
函数 | null |
请求抛出异常时触发(如网络错误、URL 无效) |
onFailure |
函数 | null |
请求失败时触发(状态码非 2xx) |
onSuccess |
函数 | null |
请求成功时触发(状态码 2xx) |
关键配置说明
method默认值注意 :与现代 AJAX 工具(如 jQuery.ajax)默认GET不同,Ajax.Request默认POST,需根据场景手动改为GET;parameters序列化 :传入对象(如{ content: '测试', id: 1 })时,Prototype 会自动调用Object.toQueryString()序列化为content=测试&id=1,并自动编码特殊字符;contentType适配 :若需发送 JSON 格式请求,需手动设置contentType: 'application/json',并将参数序列化为 JSON 字符串(parameters: JSON.stringify(data))。
2. 回调函数体系
Ajax.Request 提供了覆盖请求全生命周期的回调函数,所有回调函数均接收 Ajax.Response 对象作为唯一参数 ------ 这意味着无论成功、失败还是异常,开发者都能通过统一的对象获取请求 / 响应信息,无需区分原生 XHR 的不同状态。
回调触发时机与使用场景
| 回调函数 | 触发时机 | 典型使用场景 |
|---|---|---|
onCreate |
XHR 对象创建完成,请求尚未发送 | 初始化请求标识、记录请求开始时间 |
onSuccess |
请求完成且状态码 2xx | 解析响应数据、更新页面内容 |
onFailure |
请求完成但状态码非 2xx(如 404/500) | 提示错误信息、处理业务异常 |
onException |
请求过程中抛出异常(如网络中断、超时) | 捕获网络错误、提示重试 |
onComplete |
请求完全结束(成功 / 失败 / 异常均触发) | 隐藏加载中提示、清理请求标识 |
回调使用示例
javascript
// 基础 AJAX 请求示例
new Ajax.Request('/api/messages', {
method: 'POST',
// 请求参数(对象自动序列化)
parameters: {
content: 'Prototype AJAX 测试',
userId: 1001
},
// 请求头
requestHeaders: {
'Token': 'user_token_123456'
},
// 超时时间
timeout: 5000,
// 回调函数
onCreate: function(response) {
console.log('请求创建:', response.request.url);
// 显示加载中提示
$('loading').show();
},
onSuccess: function(response) {
console.log('请求成功:', response.status);
// 解析 JSON 响应(自动解析)
const data = response.responseJSON;
// 更新页面内容
$('message-list').update(`新增留言:${data.content}`);
},
onFailure: function(response) {
console.log('请求失败:', response.status);
alert(`请求失败,状态码:${response.status}`);
},
onException: function(response, exception) {
console.log('请求异常:', exception.message);
alert('网络异常,请重试!');
},
onComplete: function(response) {
console.log('请求完成');
// 隐藏加载中提示
$('loading').hide();
}
});
3. Ajax.Response
Ajax.Response 是 Prototype 对原生 XHR 对象的 "包装层",它整合了请求状态、响应数据、头信息等所有核心信息,提供统一的属性和方法,避免开发者直接操作原生 XHR 带来的兼容问题和解析繁琐。
核心属性
| 属性名 | 类型 | 说明 |
|---|---|---|
status |
数字 | HTTP 状态码(如 200/404/500),原生 XHR 的 status 封装 |
statusText |
字符串 | 状态码描述(如 OK/Not Found) |
responseText |
字符串 | 原生响应文本,对应 XHR 的 responseText |
responseXML |
XMLDocument | 响应 XML 文档(若响应类型为 XML),对应 XHR 的 responseXML |
responseJSON |
对象 / 数组 | 自动解析的 JSON 数据(若响应文本为合法 JSON),无需手动 JSON.parse |
request |
对象 | 对应的 Ajax.Request 实例,可获取请求配置(如 request.url) |
transport |
对象 | 原生 XHR 对象(兜底使用,一般无需操作) |
timedOut |
布尔值 | 是否超时(true 表示超时) |
核心方法
| 方法名 | 参数 | 返回值 | 说明 |
|---|---|---|---|
getHeader(name) |
字符串 | 字符串 /null |
获取指定响应头,自动兼容大小写(如 getHeader('Content-Type')) |
getAllHeaders() |
无 | 对象 | 获取所有响应头,以键值对形式返回 |
Ajax.Response 优势示例
对比原生 XHR 和 Ajax.Response 的响应处理:
javascript
// 原生 XHR 解析响应(繁琐且易出错)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let data;
try {
data = JSON.parse(xhr.responseText); // 手动解析,需捕获错误
} catch (e) {
console.log('JSON 解析失败', e);
return;
}
const contentType = xhr.getResponseHeader('Content-Type');
console.log(data, contentType);
}
}
};
// Prototype Ajax.Response(简洁且安全)
new Ajax.Request('/api/data', {
onSuccess: function(response) {
// 自动解析 JSON,无需手动处理
const data = response.responseJSON;
// 统一获取响应头,兼容大小写
const contentType = response.getHeader('content-type');
console.log(data, contentType);
}
});
三、全局响应器
单个 Ajax.Request 解决了 "单次请求" 的简化问题,而 Ajax.Responders 则解决了 "全局请求" 的管控问题 ------ 通过注册全局回调,可对页面中所有 AJAX 请求统一处理(如全局加载提示、统一错误拦截、请求日志),避免在每个请求中重复编写相同逻辑。
1. 核心原理与语法
Ajax.Responders 是 Prototype 提供的全局 AJAX 事件管理器,本质是一个 "回调注册表":
- 注册:通过
Ajax.Responders.register(object)将包含全局回调的对象加入注册表,该对象的回调会对所有Ajax.Request生效; - 注销:通过
Ajax.Responders.unregister(object)移除已注册的全局回调(需传入注册时的同一个对象,否则注销失败); - 全局回调:支持
onCreate/onComplete/onSuccess/onFailure/onException等,与Ajax.Request的回调同名,触发时机一致,参数也为Ajax.Response。
基础语法
javascript
// 定义全局回调对象
const globalAjaxHandlers = {
onCreate: function(response) {
console.log('全局:请求创建', response.request.url);
},
onComplete: function(response) {
console.log('全局:请求完成', response.status);
},
onFailure: function(response) {
console.log('全局:请求失败', response.status);
}
};
// 注册全局响应器
Ajax.Responders.register(globalAjaxHandlers);
// 注销全局响应器(按需)
// Ajax.Responders.unregister(globalAjaxHandlers);
2. 核心应用场景
Ajax.Responders 的价值在于 "批量处理通用逻辑",以下是最典型的三个场景:
场景 1:全局加载中提示
统计当前活跃的 AJAX 请求数,请求开始时显示加载提示,所有请求完成后隐藏:
javascript
// 全局加载状态管理
const loadingManager = {
activeRequests: 0, // 活跃请求数
onCreate: function() {
this.activeRequests++;
// 显示加载提示(仅当第一个请求时显示)
if (this.activeRequests === 1) {
$('loading-mask').show();
}
},
onComplete: function() {
this.activeRequests--;
// 隐藏加载提示(仅当所有请求完成时)
if (this.activeRequests <= 0) {
this.activeRequests = 0; // 避免负数
$('loading-mask').hide();
}
}
};
// 注册全局响应器
Ajax.Responders.register(loadingManager);
场景 2:统一错误拦截
对所有 AJAX 请求的失败 / 异常进行统一处理(如 401 未登录跳转登录页、500 服务器错误提示):
javascript
// 全局错误处理器
const errorHandler = {
onFailure: function(response) {
const status = response.status;
// 401:未登录,跳转登录页
if (status === 401) {
alert('登录已过期,请重新登录');
window.location.href = '/login';
}
// 403:无权限
else if (status === 403) {
alert('您无权限访问该资源');
}
// 500:服务器错误
else if (status === 500) {
alert('服务器内部错误,请稍后重试');
}
},
onException: function(response, exception) {
// 网络异常/超时,统一提示
alert('网络异常,请检查网络连接后重试');
console.error('AJAX 异常:', exception);
}
};
Ajax.Responders.register(errorHandler);
场景 3:全局请求日志
记录所有 AJAX 请求的 URL、方式、状态码、耗时,用于调试或埋点:
javascript
// 全局日志记录器
const logger = {
onCreate: function(response) {
// 记录请求开始时间
response.request.startTime = Date.now();
console.log(`[AJAX 开始] ${response.request.method} ${response.request.url}`);
},
onComplete: function(response) {
// 计算请求耗时
const duration = Date.now() - response.request.startTime;
console.log(`[AJAX 完成] ${response.request.url} - 状态码:${response.status},耗时:${duration}ms`);
}
};
Ajax.Responders.register(logger);
3. 注意事项
- 注销时机:全局响应器会持续生效,若页面有动态组件(如弹窗),组件销毁时需注销对应的全局回调,避免内存泄漏;
- 回调执行顺序 :先执行全局回调,再执行单个请求的回调(如先执行
Ajax.Responders的onSuccess,再执行Ajax.Request的onSuccess); - 避免冲突:多个全局响应器的回调会按注册顺序执行,需确保回调逻辑无冲突(如多个加载提示逻辑)。
四、完整的 AJAX 动态数据交互
以下以 "动态留言板" 为例,整合 Ajax.Request 和 Ajax.Responders,展示从请求发起、响应处理到全局管控的完整流程:
1. 页面结构
javascript
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype AJAX 留言板</title>
<script src="prototype.js"></script>
<style>
#loading-mask { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.3); text-align: center; color: white; padding-top: 200px; }
.message { margin: 10px 0; padding: 10px; border: 1px solid #ccc; }
.error { color: red; margin: 10px 0; }
</style>
</head>
<body>
<!-- 全局加载遮罩 -->
<div id="loading-mask">加载中...</div>
<!-- 错误提示 -->
<div id="error-tip" class="error"></div>
<!-- 留言列表 -->
<div id="message-list"></div>
<!-- 留言表单 -->
<form id="message-form">
<input type="text" id="message-input" placeholder="输入留言">
<button type="submit">提交</button>
</form>
<script>
// 第一步:注册全局响应器(加载提示+统一错误处理)
const globalHandlers = {
activeRequests: 0,
onCreate: function() {
this.activeRequests++;
$('loading-mask').show();
// 清空之前的错误提示
$('error-tip').update('');
},
onComplete: function() {
this.activeRequests--;
if (this.activeRequests <= 0) {
this.activeRequests = 0;
$('loading-mask').hide();
}
},
onFailure: function(response) {
const status = response.status;
let tip = '';
if (status === 401) tip = '登录过期,请重新登录';
else if (status === 500) tip = '服务器错误,请稍后重试';
else tip = `请求失败(${status})`;
$('error-tip').update(tip);
},
onException: function() {
$('error-tip').update('网络异常,请检查网络');
}
};
Ajax.Responders.register(globalHandlers);
// 第二步:页面初始化,加载留言列表
Event.observe(document, 'dom:loaded', function() {
loadMessages(); // 加载已有留言
bindFormSubmit(); // 绑定表单提交事件
});
// 第三步:加载留言列表(GET 请求)
function loadMessages() {
new Ajax.Request('/api/messages', {
method: 'GET', // 手动改为 GET(默认 POST)
onSuccess: function(response) {
const messages = response.responseJSON || [];
// 渲染留言列表
const list = $('message-list');
list.update(''); // 清空原有内容
messages.forEach(msg => {
const msgEl = new Element('div', { className: 'message' });
msgEl.update(`[${msg.time}] ${msg.content}`);
list.insert({ bottom: msgEl });
});
}
});
}
// 第四步:绑定表单提交,新增留言(POST 请求)
function bindFormSubmit() {
Event.observe('message-form', 'submit', function(event) {
event.stop(); // 阻止表单默认提交
const input = $('message-input');
const content = input.value.trim();
if (!content) {
$('error-tip').update('请输入留言内容');
return;
}
// 发送 POST 请求新增留言
new Ajax.Request('/api/messages', {
method: 'POST',
parameters: { content: content },
requestHeaders: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
onSuccess: function() {
input.value = ''; // 清空输入框
loadMessages(); // 重新加载留言列表
}
});
});
}
</script>
</body>
</html>
2. 示例解析
- 全局管控 :通过
Ajax.Responders实现全局加载提示和错误提示,无需在loadMessages和表单提交请求中重复编写; - 请求简化 :
Ajax.Request自动处理参数序列化、XHR 创建、响应解析,只需配置核心参数和回调; - 响应处理 :
response.responseJSON自动解析 JSON 数据,无需手动处理解析错误; - 流程闭环:表单提交后重新加载留言列表,实现 "提交 - 刷新" 的动态数据交互,全程无页面刷新。
五、进阶技巧与注意事项
1. 发送 JSON 格式请求
默认 contentType 是 application/x-www-form-urlencoded,若需发送 JSON 格式请求,需手动配置:
javascript
new Ajax.Request('/api/user', {
method: 'POST',
// 参数需序列化为 JSON 字符串
parameters: JSON.stringify({ name: '张三', age: 20 }),
// 设置 JSON 类型请求头
requestHeaders: {
'Content-Type': 'application/json; charset=UTF-8'
},
onSuccess: function(response) {
const data = response.responseJSON;
console.log(data);
}
});
2. 处理超时请求
通过 timeout 配置超时时间,并在 onException 中捕获超时:
javascript
new Ajax.Request('/api/slow', {
timeout: 3000, // 3 秒超时
onException: function(response, exception) {
if (response.timedOut) {
alert('请求超时,请重试');
} else {
alert('网络异常');
}
}
});
3. 取消正在进行的请求
通过 Ajax.Request 实例的 transport.abort() 取消请求:
javascript
// 保存请求实例
const request = new Ajax.Request('/api/data', {
onSuccess: function() {
console.log('请求成功');
}
});
// 取消请求(如点击取消按钮)
Event.observe('cancel-btn', 'click', function() {
request.transport.abort(); // 调用原生 XHR 的 abort 方法
console.log('请求已取消');
});
4. 避免重复请求
对同一接口的重复请求(如快速点击提交按钮),可通过标识控制:
javascript
let isSubmitting = false; // 请求标识
Event.observe('submit-btn', 'click', function() {
if (isSubmitting) return; // 已有请求,直接返回
isSubmitting = true;
new Ajax.Request('/api/submit', {
onComplete: function() {
isSubmitting = false; // 请求完成后重置标识
}
});
});
5. 兼容跨域请求
Prototype 的 Ajax.Request 基于原生 XHR,因此跨域需满足 CORS 规则(服务器配置跨域头),若需兼容旧浏览器的 JSONP,可使用 Prototype 配套的 Ajax.JSONP 方法:
javascript
运行
new Ajax.JSONP({
url: 'https://cross-domain-api.com/data',
parameters: { callback: 'onJSONPResponse' },
onSuccess: function(data) {
console.log('JSONP 响应:', data);
}
});
最后小结
Ajax.Request 和 Ajax.Responders 作为 Prototype.js 处理异步请求的核心能力,虽然随着 Fetch API、Axios 等现代工具的出现逐渐退出主流,但它的设计理念至今仍深刻影响着前端异步请求的开发范式:
1. 时代价值
- 首次将 AJAX 请求从 "底层 XHR 操作" 抽象为 "配置化接口",大幅降低了异步开发的门槛;
- 提出了 "标准化响应对象" 和 "全局请求管控" 的思路,后续 jQuery.ajax、Axios 均继承了这一设计;
- 验证了 "封装兼容逻辑,提供统一接口" 是前端工具库的核心价值 ------ 开发者无需关注 XHR 兼容、参数序列化等底层细节,只需聚焦业务逻辑。
2. 核心启示
- 异步请求的核心痛点是 "配置繁琐、响应碎片化、全局管控难":好的工具应围绕这三点做封装;
- 标准化是效率的关键:统一的响应对象(如
Ajax.Response)、统一的回调体系,能大幅降低代码复杂度; - 全局管控优于重复编写:通用逻辑(加载提示、错误处理)应通过全局钩子统一处理,而非在每个请求中重复。
即使在现代前端开发中,Ajax.Request 所解决的核心问题依然存在 ------Fetch API 虽简化了 XHR,但仍需手动处理参数序列化、响应解析、全局拦截等问题,而 Axios 等工具的核心设计,正是对 Ajax.Request 理念的延续和升级。理解 Ajax.Request 的设计与实现,能帮助开发者更深刻地理解前端异步请求的本质,无论是使用原生 Fetch 还是现代请求库,都能写出更健壮、更高效的异步代码。