第111天:Ajax之jQuery實現方法
由於jQuery中的Ajax方法是用了內置的deferred模塊,是Promise模式的一種實現,而我們這裏沒有講過,所以我們就不使用這一模式啦。
我們只定義一個Ajax方法,他可以簡單的get,post,jsonp請求就可以了。
一、jQuery語法格式
1 var ajax = function () { 2 3 // 做一些初始化,定義一些私有函數等 4 5 return function () { 6 // ajax主體代碼 7 } 8 9 }() 10 11 12 ajax({ 13 url: myUrl, 14 type: ‘get‘, 15 dataType: ‘json‘, 16 timeout: 1000, 17 success: function (data, status) { 18 console.log(data) 19 }, 20 fail: function (err, status) { 21 console.log(err) 22 } 23 })
二、初始化屬性
Ajax方法需要傳遞一個對象進去,這個對象中我們可以定義一些我們希望的屬性,我們就必須初始一下各種屬性
1 //默認請求參數 2 var _options = { 3 url: null, // 請求連接 4 type: ‘GET‘, // 請求類型 5 data: null, // post時請求體 6 dataType: ‘text‘, // 返回請求的類型,有text/json兩種 7 jsonp: ‘callback‘, // jsonp請求的標誌,一般不改動 8 jsonpCallback: ‘jsonpCallback‘, //jsonp請求的函數名 9 async: true, // 是否異步 10 cache: true, // 是否緩存 11 timeout:null, // 設置請求超時 12 contentType: ‘application/x-www-form-urlencoded‘, 13 success: null, // 請求成功回調函數 14 fail: null // 請求失敗回調 15 }
三、Ajax主體函數
以上我們定義了一大串請求有關的數據,接下來我們就開始Ajax主體函數的書寫,現在的Ajax方法是這樣了
1 var ajax = function () { 2 3 //默認請求參數 4 var _options = { 5 url: null, 6 type: ‘GET‘, 7 data: null, 8 dataType: ‘text‘, 9 jsonp: ‘callback‘, 10 jsonpCallback: ‘jsonpCallback‘, 11 async: true, 12 cache: true, 13 timeout:null, 14 contentType: ‘application/x-www-form-urlencoded‘, 15 success: null, 16 fail: null 17 } 18 // ... 19 return function (options) { 20 // ... 21 } 22 }()
四、內部繼承
我們可以想一下,ajax方法傳遞一個對象進來,我們需要把我們設置的這個對象上的屬性來覆蓋掉初始化_options上面的那些屬性呢,肯定需要。那下面我們先寫一個簡單的繼承,如下:
1 var ajax = function () { 2 3 //默認請求參數 4 var _options = { 5 url: null, 6 type: ‘GET‘, 7 data: null, 8 dataType: ‘text‘, 9 jsonp: ‘callback‘, 10 jsonpCallback: ‘jsonpCallback‘, 11 async: true, 12 cache: true, 13 timeout:null, 14 contentType: ‘application/x-www-form-urlencoded‘, 15 success: null, 16 fail: null 17 } 18 // 內部使用的繼承方法 19 var _extend = function(target,options) { 20 if( typeof target !== ‘object‘ || typeof options !== ‘object‘ ) { 21 return; 22 } 23 var copy ,clone, name; 24 for( name in options ) { 25 if(options.hasOwnProperty(name) && !target.hasOwnProperty(name)) { 26 target[name] = options[name]; 27 } 28 } 29 return target; 30 }; 31 32 33 // ... 34 return function (options) { 35 36 // 沒有傳參或者沒有url,拋出錯誤 37 if( !options || !options.url ) { 38 throw(‘參數錯誤!‘); 39 } 40 41 // 繼承操作 42 options.type = options.type.toUpperCase(); 43 _extend(options,_options); 44 // ... 45 } 46 }()
這個繼承方法,我們是把初始化的_options繼承到了options,為什麽呢?因為我們這個_options對象不在ajax方法內部,我們需要使用它,但我們不能改變他,如果改變了他,下一次使用ajax方法將會崩潰。因此,我們僅僅是把配置的options對象沒有的屬性設置為初始值。
五、jsonp請求
jsonp請求不是xhr請求,他是將請求url當做script標簽的src值插入到頁面body中去實現的,我們先把jsonp請求處理一下再開始建立xhr請求的代碼吧。
1 var ajax = function () { 2 3 //默認請求參數 4 var _options = { 5 url: null, 6 type: ‘GET‘, 7 data: null, 8 dataType: ‘text‘, 9 jsonp: ‘callback‘, 10 jsonpCallback: ‘jsonpCallback‘, 11 async: true, 12 cache: true, 13 timeout:null, 14 contentType: ‘application/x-www-form-urlencoded‘, 15 success: null, 16 fail: null 17 } 18 // 內部使用的繼承方法 19 var _extend = function(target,options) { 20 if( typeof target !== ‘object‘ || typeof options !== ‘object‘ ) { 21 return; 22 } 23 var copy ,clone, name; 24 for( name in options ) { 25 if(options.hasOwnProperty(name) && !target.hasOwnProperty(name)) { 26 target[name] = options[name]; 27 } 28 } 29 return target; 30 }; 31 32 // jsonp處理函數 33 function _sendJsonpRequest(url,callbackName,succCallback) { 34 35 var script = document.createElement(‘script‘); 36 37 script.type="text/javascript"; 38 script.src=url; 39 40 document.body.appendChild(script); 41 // 如果用戶自己定義了回調函數,就用自己定義的,否則,調用success函數 42 window[callbackName] = window[callbackName] || succCallback; 43 44 } 45 46 // ... 47 return function (options) { 48 49 // 沒有傳參或者沒有url,拋出錯誤 50 if( !options || !options.url ) { 51 throw(‘參數錯誤!‘); 52 } 53 54 // 繼承操作 55 options.type = options.type.toUpperCase(); 56 _extend(options,_options); 57 58 /*jsonp部分,直接返回*/ 59 if( options.dataType === ‘jsonp‘ ) { 60 var jsonpUrl = options.url.indexOf(‘?‘) > -1 ? options.url: options.url + 61 ‘?‘ + options.jsonp+ ‘=‘ + options.jsonpCallback; 62 63 return _sendJsonpRequest(jsonpUrl,options.jsonpCallback,options.success); 64 65 } 66 // ... 67 } 68 }()
我們定義了一個_sendJsonpRequest函數,這個函數接收三個參數,第一個是jsonpUrl,第二個是jsonp的回調函數名,第三個是成功回調函數,我們在這個函數內建立一個src為jsonpUrl的script元素插入到body中,同時,確定了回調函數(如果我們定義了jsonpCallback函數就調用它,如果沒有就調用success回調,一般情況我們不去定義全局的jsonpCallback函數而傳遞success回調來完成jsonp請求)。
六、xhr請求處理
好,處理好jsonp請求後,我們開始處理xhr請求了。
1 var ajax = function () { 2 3 //默認請求參數 4 var _options = { 5 url: null, 6 type: ‘GET‘, 7 data: null, 8 dataType: ‘text‘, 9 jsonp: ‘callback‘, 10 jsonpCallback: ‘jsonpCallback‘, 11 async: true, 12 cache: true, 13 timeout:null, 14 contentType: ‘application/x-www-form-urlencoded‘, 15 success: null, 16 fail: null 17 } 18 // 內部使用的繼承方法 19 var _extend = function(target,options) { 20 if( typeof target !== ‘object‘ || typeof options !== ‘object‘ ) { 21 return; 22 } 23 var copy ,clone, name; 24 for( name in options ) { 25 if(options.hasOwnProperty(name) && !target.hasOwnProperty(name)) { 26 target[name] = options[name]; 27 } 28 } 29 return target; 30 }; 31 32 // jsonp處理函數 33 function _sendJsonpRequest(url,callbackName,succCallback) { 34 35 var script = document.createElement(‘script‘); 36 37 script.type="text/javascript"; 38 script.src=url; 39 40 document.body.appendChild(script); 41 // 如果用戶自己定義了回調函數,就用自己定義的,否則,調用success函數 42 window[callbackName] = window[callbackName] || succCallback; 43 44 } 45 46 // json轉化為字符串 47 var _param = function(data) { 48 var str = ‘‘; 49 if( !data || _empty(data)) { 50 return str; 51 } 52 for(var key in data) { 53 str += key + ‘=‘+ data[key]+‘&‘ 54 } 55 str = str.slice(0,-1); 56 return str; 57 } 58 //判斷對象是否為空 59 var _empty = function(obj) { 60 for(var key in obj) { 61 return false; 62 } 63 return true; 64 } 65 66 // ... 67 return function (options) { 68 69 // 沒有傳參或者沒有url,拋出錯誤 70 if( !options || !options.url ) { 71 throw(‘參數錯誤!‘); 72 } 73 74 // 繼承操作 75 options.type = options.type.toUpperCase(); 76 _extend(options,_options); 77 78 /*jsonp部分,直接返回*/ 79 if( options.dataType === ‘jsonp‘ ) { 80 var jsonpUrl = options.url.indexOf(‘?‘) > -1 ? options.url: options.url + 81 ‘?‘ + options.jsonp+ ‘=‘ + options.jsonpCallback; 82 83 return _sendJsonpRequest(jsonpUrl,options.jsonpCallback,options.success); 84 85 } 86 87 //XMLHttpRequest傳參無影響 88 var xhr = new (window.XMLHttpRequest || ActiveXObject)(‘Microsoft.XMLHTTP‘); 89 // get搜索字符串 90 var search = ‘‘; 91 92 // 將data序列化 93 var param= _param(options.data) 94 95 if( options.type === ‘GET‘ ) { 96 search = (options.url.indexOf(‘?‘) > -1 ? ‘&‘ : ‘?‘) + param; 97 if(!options.cache) { 98 search += ‘&radom=‘+Math.random(); 99 } 100 101 param = null; 102 } 103 104 // ... 105 } 106 }()
首先,兼容IE創建xhr對象,XMLHttpRequest構造函數傳遞參數是無影響,然後我們定義了兩個輔助變量:search、param,前者用於get請求的查詢字串,後者用於post請求的send內容,我們定義了一個_param方法來講對象轉換為send方法參數的模式,就如你看到的那樣,下面我們做了get與post之間合理的search、param的賦值工作。接下來我們就可以發送請求書寫最激動人心的內容了。
最終的代碼如下
1 ; 2 3 var ajax = function () { 4 5 //默認請求參數 6 var _options = { 7 url: null, 8 type: ‘GET‘, 9 data: null, 10 dataType: ‘text‘, 11 jsonp: ‘callback‘, 12 jsonpCallback: ‘jsonpCallback‘, 13 async: true, 14 cache: true, 15 timeout:null, 16 contentType: ‘application/x-www-form-urlencoded‘, 17 success: null, 18 fail: null 19 } 20 21 22 // json轉化為字符串 23 var _param = function(data) { 24 var str = ‘‘; 25 if( !data || _empty(data)) { 26 return str; 27 } 28 for(var key in data) { 29 str += key + ‘=‘+ data[key]+‘&‘ 30 } 31 str = str.slice(0,-1); 32 return str; 33 } 34 //判斷對象是否為空 35 var _empty = function(obj) { 36 for(var key in obj) { 37 return false; 38 } 39 return true; 40 } 41 42 var _extend = function(target,options) { 43 if( typeof target !== ‘object‘ || typeof options !== ‘object‘ ) { 44 return; 45 } 46 var copy ,clone, name; 47 for( name in options ) { 48 if(options.hasOwnProperty(name) && !target.hasOwnProperty(name)) { 49 target[name] = options[name]; 50 } 51 } 52 return target; 53 }; 54 55 // 自定義text轉化json格式 56 var parseJSON = function(text) { 57 if(typeof text !== ‘string‘) { 58 return; 59 } 60 if( JSON && JSON.parse ) { 61 return JSON.parse(text); 62 } 63 return (new Function(‘return ‘+text))(); 64 } 65 66 // jsonp處理函數 67 function _sendJsonpRequest(url,callbackName,succCallback) { 68 69 var script = document.createElement(‘script‘); 70 71 script.type="text/javascript"; 72 script.src=url; 73 74 document.body.appendChild(script); 75 // 如果用戶自己定義了回調函數,就用自己定義的,否則,調用success函數 76 window[callbackName] = window[callbackName] || succCallback; 77 78 } 79 80 81 return function (options) { 82 83 // 沒有傳參或者沒有url,拋出錯誤 84 if( !options || !options.url ) { 85 throw(‘參數錯誤!‘); 86 } 87 88 // 繼承操作 89 options.type = options.type.toUpperCase(); 90 _extend(options,_options); 91 92 /*jsonp部分,直接返回*/ 93 if( options.dataType === ‘jsonp‘ ) { 94 var jsonpUrl = options.url.indexOf(‘?‘) > -1 ? options.url: options.url + 95 ‘?‘ + options.jsonp+ ‘=‘ + options.jsonpCallback; 96 97 _sendJsonpRequest(jsonpUrl,options.jsonpCallback,options.success); 98 99 return; 100 } 101 102 //XMLHttpRequest傳參無影響 103 var xhr = new (window.XMLHttpRequest || ActiveXObject)(‘Microsoft.XMLHTTP‘); 104 105 // get搜索字符串 106 var search = ‘‘; 107 108 // 將data序列化 109 var param= _param(options.data) 110 111 if( options.type === ‘GET‘ ) { 112 search = (options.url.indexOf(‘?‘) > -1 ? ‘&‘ : ‘?‘) + param; 113 if(!options.cache) { 114 search += ‘&radom=‘+Math.random(); 115 } 116 117 param = null; 118 } 119 120 xhr.open( options.type, options.url + search, options.async ); 121 122 xhr.onreadystatechange = function() { 123 if( xhr.readyState == 4 ) { 124 if( xhr.status >= 200 && xhr.status < 300 || xhr.status == 304 ) { 125 var text = xhr.responseText; 126 // json格式轉換 127 if(options.dataType == ‘json‘) { 128 text = parseJSON(text) 129 } 130 131 if( typeof options.success === ‘function‘) { 132 133 options.success(text,xhr.status) 134 } 135 136 }else { 137 138 if(typeof options.fail === ‘function‘) { 139 options.fail(‘獲取失敗‘, 500) 140 } 141 142 } 143 } 144 } 145 146 xhr.setRequestHeader(‘content-type‘,options.contentType); 147 // get請求時param時null 148 xhr.send(param); 149 150 // 如果設置了超時,就定義 151 if(typeof options.timeout === ‘number‘) { 152 // ie9+ 153 if( xhr.timeout ) { 154 xhr.timeout = options.timeout; 155 }else { 156 setTimeout(function() { 157 xhr.abort(); 158 },options.timeout) 159 } 160 } 161 } 162 163 }()
可以看到,我們很熟悉的xhr代碼,在這裏,我們需要寫一個解析返回字串形成json格式對象的方法parseJSON,類似於jq中的parseJSON方法,如上所示。
我們還需要設置超時代碼,如果設置了請求超時,我們就如上定義。
註意:上面代碼中,由於懶,設置請求頭一行並沒有判斷是否在post請求下,你可以自己設置
第111天:Ajax之jQuery實現方法