原生JS实现异步数据请求:AJAX与JSONP封装详解
本文详细讲解如何在无框架依赖下使用原生JavaScript进行异步数据请求。重点解析AJAX技术核心XMLHttpRequest对象的状态机与IE兼容处理,以及利用script标签src属性实现跨域的JSONP机制。通过封装统一接口,根据dataType自动选择请求方式,提供完整的代码实现与参数说明。
原生JS异步请求概述
项目开发中常需处理异步数据交互。若环境未引入第三方框架,则需依赖原生JavaScript实现异步请求。主要存在两种实现路径:AJAX与JSONP。本文将对这两种原生异步请求方式进行基础封装与解析。
AJAX技术实现
AJAX
AJAX(Asynchronous JavaScript and XML)技术允许在不刷新整个页面的前提下更新局部数据。其核心组件为XMLHttpRequest对象,标准请求流程包含以下关键步骤:
- 创建XMLHttpRequest对象实例
- 配置服务器连接参数(open)
- 执行发送请求操作(send)
- 监听响应数据接收状态(onreadystatechange)
AJAX核心代码封装
具体代码实现如下。
/**
* 通过JSON的方式请求
* @param {[type]} params [description]
* @return {[type]} [description]
*/
ajaxJSON(params) {
params.type = (params.type || 'GET').toUpperCase();
params.data = params.data || {};
var formatedParams = this.formateParams(params.data, params.cache);
var xhr;
//创建XMLHttpRequest对象
if (window.XMLHttpRequest) {
//非IE6
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
//异步状态发生改变,接收响应数据
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
if (!!params.success) {
if (typeof xhr.responseText == 'string') {
params.success(JSON.parse(xhr.responseText));
} else {
params.success(xhr.responseText);
}
}
} else {
params.error && params.error(status);
}
}
if (params.type == 'GET') {
//连接服务器
xhr.open('GET', (!!formatedParams ? params.url + '?' + formatedParams : params.url), true);
//发送请求
xhr.send();
} else {
//连接服务器
xhr.open('POST', params.url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
//发送请求
xhr.send(formatedParams);
}
},
/**
* 格式化数据
* @param {Obj} data 需要格式化的数据
* @param {Boolean} isCache 是否加入随机参数
* @return {String} 返回的字符串
*/
formateParams: function(data, isCache) {
var arr = [];
for (var name in data) {
arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
}
if (isCache) {
arr.push('v=' + (new Date()).getTime());
}
return arr.join('&');
}
在IE7及以上版本浏览器中,原生支持XMLHttpRequest对象,可直接通过var oAjax = new XMLHttpRequest()实例化。对于IE6及更早版本,需通过MSXML库中的ActiveXObject对象来实现XHR功能。
使用xhr对象的open方法建立服务器连接,该方法接收三个参数:请求方法、请求URL地址以及是否启用异步模式(通常设为异步)。请求方法主要分为GET与POST:GET方式将数据附加于URL提交至服务器;POST方式则将数据作为send方法的参数进行传输。
需为xhr对象绑定状态改变事件监听器onreadystatechange,用于监测readyState属性的变化。异步请求成功后,readyState值将从0递增至4,并触发相应事件。各状态码含义如下:
0 (未初始化):对象已创建,但尚未调用open方法。 1 (初始化):对象已创建,但尚未调用send方法。 2 (发送数据):send方法已调用,当前状态及HTTP头信息尚不明确。 3 (数据传送中):已接收部分响应数据,因响应完整内容及HTTP头未完全就绪,此时通过responseBody或responseText获取数据可能产生错误。 4 (完成):数据接收完毕,此时可通过responseBody或responseText获取完整响应数据。
在readystatechange事件处理逻辑中,首先判断响应是否已完全接收(readyState为4),随后验证服务器处理结果。xhr.status属性返回HTTP状态码,以2开头的状态码代表请求成功。状态码304表示从缓存读取数据;由于前述代码在每次请求时均附加随机数参数以禁用缓存,因此无需特别处理304状态。
JSONP跨域机制
JSONP
若继续使用XMLHttpRequest对象发起跨域请求,即使调用send方法,xhr状态值将始终为0,且不会触发onreadystatechange事件。此时需采用JSONP(JSON with Padding)方式进行跨域数据获取。
JSONP利用script标签支持跨域加载资源的特性实现请求。通过设置script标签的src属性指向服务器地址,服务器返回JavaScript代码片段。浏览器接收响应后直接执行该代码,其原理类似于通过script标签引用外部脚本文件。
JSONP请求由两部分构成:回调函数名与数据。回调函数名通常由客户端生成并作为参数传递给服务器端,服务器端将数据封装在该函数调用中返回。
例如,客户端创建script标签并将src设置为http://www.test.com/json/?callback=process,从而发起请求。服务端将待返回数据封装为函数参数形式返回,格式如”process({‘name':'xieyufei'})”。客户端接收到响应后,因请求源为script标签,系统会自动执行process方法并传入对应参数。
JSONP实现细节
具体代码实现如下。
/**
* 通过JSONP的方式请求
* @param {[type]} params [description]
* @return {[type]} [description]
*/
ajaxJSONP(params) {
params.data = params.data || {};
params.jsonp = params.jsonp || 'callback';
// 设置传递给后台的回调参数名和参数值
var callbackName = 'jsonp_' + (new Date()).getTime();
params.data[params.jsonp] = callbackName;
var formatedParams = this.formateParams(params.data, params.cache);
//创建script标签并插入到页面中
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
head.appendChild(script);
//创建jsonp回调函数
window[callbackName] = function(json) {
head.removeChild(script);
clearTimeout(script.timer);
window[callbackName] = null;
params.success && params.success(json);
};
//发送请求
script.src = (!!formatedParams ? params.url + '?' + formatedParams : params.url);
//为了得知此次请求是否成功,设置超时处理
if (params.time) {
script.timer = setTimeout(function() {
window[callbackName] = null;
head.removeChild(script);
params.error && params.error({
message: '超时'
});
}, params.time);
}
}
为script标签设置src属性会立即触发浏览器发送请求,但script标签仅能发起一次请求,无法复用。因此,每次请求完成后需移除对应的script标签。在发送请求前,需在页面全局作用域绑定回调函数,待数据请求成功时由服务器返回的代码调用该回调函数。
统一请求接口封装
总结
将上述两种异步请求方式整合,通过判断dataType参数自动选择AJAX或JSONP策略。完整封装代码展示如下。
var xyfAjax = {
ajax: function(params) {
params = params || {};
params.cache = params.cache || false;
if (!params.url) {
throw new Error('参数不合法');
}
params.dataType = (params.dataType || 'json').toLowerCase();
if (params.dataType == 'jsonp') {
this.ajaxJSONP(params);
} else if (params.dataType == 'json') {
this.ajaxJSON(params);
}
},
/**
* 通过JSONP的方式请求
* @param {[type]} params [description]
* @return {[type]} [description]
*/
ajaxJSONP(params) {
params.data = params.data || {};
params.jsonp = params.jsonp || 'callback';
// 设置传递给后台的回调参数名和参数值
var callbackName = 'jsonp_' + (new Date()).getTime();
params.data[params.jsonp] = callbackName;
var formatedParams = this.formateParams(params.data, params.cache);
//创建script标签并插入到页面中
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
head.appendChild(script);
//创建jsonp回调函数
window[callbackName] = function(json) {
head.removeChild(script);
clearTimeout(script.timer);
window[callbackName] = null;
params.success && params.success(json);
};
//发送请求
script.src = (!!formatedParams ? params.url + '?' + formatedParams : params.url);
//为了得知此次请求是否成功,设置超时处理
if (params.time) {
script.timer = setTimeout(function() {
window[callbackName] = null;
head.removeChild(script);
params.error && params.error({
message: '超时'
});
}, params.time);
}
},
/**
* 通过JSON的方式请求
* @param {[type]} params [description]
* @return {[type]} [description]
*/
ajaxJSON(params) {
params.type = (params.type || 'GET').toUpperCase();
params.data = params.data || {};
var formatedParams = this.formateParams(params.data, params.cache);
var xhr;
//创建XMLHttpRequest对象
if (window.XMLHttpRequest) {
//非IE6
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
//异步状态发生改变,接收响应数据
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
if (!!params.success) {
if (typeof xhr.responseText == 'string') {
params.success(JSON.parse(xhr.responseText));
} else {
params.success(xhr.responseText);
}
}
} else {
params.error && params.error(status);
}
}
if (params.type == 'GET') {
//连接服务器
xhr.open('GET', (!!formatedParams ? params.url + '?' + formatedParams : params.url), true);
//发送请求
xhr.send(null);
} else {
//连接服务器
xhr.open('POST', params.url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
//发送请求
xhr.send(formatedParams);
}
},
/**
* 格式化数据
* @param {Obj} data 需要格式化的数据
* @param {Boolean} isCache 是否加入随机参数
* @return {String} 返回的字符串
*/
formateParams: function(data, isCache) {
var arr = [];
for (var name in data) {
arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
}
if (isCache) {
arr.push('v=' + (new Date()).getTime());
}
return arr.join('&');
}
}
xyfAjax.ajax({
url:'http://www.xieyufei.com',
type:'get', //or post
dataType:'json', //or jsonp
data:{
name:'xyf'
},
success: function(data){
console.log(data)
}
})
本文介绍了原生JavaScript实现异步数据请求的核心原理与封装方法,希望能为相关学习提供辅助,欢迎继续支持本站内容更新。
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28
