JavaScript JSON 与 AJAX

JSONP 回调函数
1. 对于普通 AJAX 请求我们可以通过监听 XMLHttpRequest 的 readystatechange 事件,判断 readystate 和 status 来知晓请求和响应是否完成,以执行成功回调或出错回调
2. JSONP 方式本质上是利用 script 标签的 src 进行请求,响应情况如下:
• 如果 src 指向资源存在,且其返回的字符串被当成 JS 代码成功执行
即页面内定义好的函数被成功执行,该函数内的成功回调函数可以通过参数拿到数据进行处理
• 如果 src 指向的目的资源访问不到
script 标签会触发 error 事件,监听此事件可以获得执行出错回调的时机

var script = document.createElement('script');
script.onerror = funtion() {
// 执行出错回调函数
}


• 如果 src 指向资源存在,返回的字符串会因为是 script 标签而被执行,执行过程中出错
在执行成功回调函数前,对 script 标签对象添加一个标记属性,监听 script 的 load 事件发生时对象是否有该标签属性
因为 onload 函数在 script 标签加载完成后执行,script 标签在其引入的代码执行后,才会响应 onload 处理函数,通过判断标记属性是否添加成功,可以知晓 script 标签引入的代码是否成功执行,如果标记属性为 undefined,则执行出错回调

var script = document.createElement('script');
window.callback = function (res) {
script['jsonp'] = 1;
// 执行成功回调函数
}
script.onload = function () {
if (typeof script['jsonp'] === 'undefined') {
// 执行出错回调函数
}
}

  


3. 需要注意的是,IE8 及以下 script 标签对象不支持 onerror,也不支持 onload,但支持 onreadystatechange
通过判断 readystate 来知晓 script 标签的加载状态,当 readystate 为 loaded 或 complete 时,表示 script 标签加载完成,即 script 标签引入的代码已经执行,同样的,在成功回调函数前为 script 对象添加标记属性,通过判断标记属性是否添加成功,可以知晓 script 标签引入的代码是否成功执行,如果标记属性为 undefined,则执行出错回调

script.onreadystatechange = function () {
// 正则表达式判断 readystate 是否为 loaded 或 complete
if (/loaded|complete/i.test(this.readyState)) {
if (typeof script['jsonp'] === 'undefined') {
// 执行出错回调函数
}
}
}}

  


4. 函数名动态生成,利用 onload 配合 onreadystate 判断加载状态,执行完毕后 delete 对应函数,并 remove 对应 script 标签节点
在自己封装 JSONP 函数时,我们可能会在 window 对象下动态添加函数如 callback,这样 script 的 src 指定资源返回形如 callback('数据') 的字符串数据,就可以直接执行此函数并获取数据,但是我们在优化 JSONP 函数时,会希望将动态创建的函数删除,在 IE8 中 delete window 下的属性会报不支持,我们可以在 Window.prototype 上添加函数,同样可以在直接执行,且支持 delete
AJAX 与 JSONP 的封装
1. 封装一个 ajax 函数,支持 get、post、jsonp 三种形式的请求,以对象形式传入参数
2. 配置项

var opt = {
type: 'get', 
url: 'http://...',
data: { // 数据使用对象形式
name: 'zzh',
age: '21'
},
async: true, // 默认 true
success: function(res) {

},
error: function() {

},
timeout: 3000 // 默认 60000
}
3. 代码
function ajax(option) {
// 设置默认参数
var opt = {
type: option.type.toUpperCase(),
url: option.url,
data: option.data || null,
async: option.async || false,
success: option.success,
error: option.error,
timeout: option.timeout || 60000
};
// 用于 jsonp 的回调函数名
var callback = 'callback' + new Date().getTime();

var type = opt.type,
success = opt.success,
error = opt.error,
data = parseData(opt.data); // 将 data 对象装换成查询字符串

if (type === 'GET' || type === 'POST') {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
success && success(xhr.responseText, xhr.status);
} else {
error && error(xhr.status);
}
}
if (type == 'GET') {
opt.url += data;
data = null;
}
xhr.open(type, opt.url, opt.async);
xhr.send(data);
setTimeout(function () {
xhr.abort();
console.error(opt.url + '请求超时');
}, opt.timeout);
} else if (type === 'JSONP') {
var script = document.createElement('script');
script.src = opt.url + data;
// 选则存放在 Window 原型上,window 下可以使用
// 如果直接存放在 window 上,IE8 window 属性不支持 dalete
Window.prototype[callback] = function (res) {
script['jsonp'] = 1;
success && success(res);
}
document.body.appendChild(script);

  

// -[1,] 在 IE8 返回 NaN,IE9 及以上返回 -1
if (-[1,]) {
// IE9 及以上支持 onerror
// onerror 用于请求失败,未执行 callback
// onload 用于请求成功,但执行 callback 出错

script.onerror = script.onload = function () {
if (typeof script['jsonp'] === 'undefined') {
error && error();
}
script.parentNode.removeChild(script);
delete Window.prototype[callback];
}
} else {
// script.onreadystatechange 兼容 IE8
script.onreadystatechange = function () {
// -[1,] 在 IE8 返回 NaN,IE9 及以上返回 -1
if (/loaded|complete/i.test(this.readyState)) {
if (typeof script['jsonp'] === 'undefined') {
error && error();
}
script.parentNode.removeChild(script);
delete Window.prototype[callback];
}
}
}

}

function parseData(data) {
var arr = [],
str;
if (type === 'GET') {
str = '?';
} else if (type === 'POST') {
str = '';
} else if (type === 'JSONP') {
str = '?callback=' + callback + '&';
}
for (var k in data) {
arr.push(k + '=' + data[k]);
}
return str + arr.join('&');
}
}
// 使用示例
ajax({
type: 'jsonp',
url: 'http://127.0.0.1:8888/',
data: {
name: 'jett',
age: 22
},
success: function (res) {
console.log('接收数据:' + res);
},
error: function () {
console.log('error() 执行了');
}
})

  

posted @ 2021-01-19 19:27  SnowyL  阅读(45)  评论(0)    收藏  举报