1. 程式人生 > 實用技巧 >JavaScript JSON 與 AJAX

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() 執行了');
}
})