在 WEB 開發中,常常見到諸如 AJAX、JSON、JSONP 這些名詞。三者看起來很像,不少同窗尤爲是沒有系統瞭解過前端技術體系的同窗,日常只是藉助相似 JQuery 這類庫封裝好的函數使用而已,並不瞭解其原理。但這三種東西具體是什麼,有什麼關係和區別卻經常說不清楚。javascript
接下來,會簡要介紹一下三者的的含義,重點闡述 JSONP 的來源和原理,以及爲何 JSONP 不是 AJAX。html
Ajax isn’t a technology. It’s really several technologies, each flourishing in its own right, coming together in powerful new ways. Ajax incorporates:前端
standards-based presentation using XHTML and CSS;java
dynamic display and interaction using the Document Object Model;web
data interchange and manipulation using XML and XSLT;ajax
asynchronous data retrieval using XMLHttpRequest;json
and JavaScript binding everything together.跨域
異步 JavaScript + XML,是在 2005 年由 Jesse James Garrett 提出的一個術語。 AJAX 並不是特指某種技術, 描述的是一種結合使用大量已有技術的方式, 包括: HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 還有最重要的 XMLHttpRequest 對象.數組
儘管在 AJAX 中 X 表明 XML, 但如今更多的在使用 JSON,由於 JSON 具備不少優點,好比更輕量而且是 JavaScript 的一部分。在 AJAX 模型中 JSON 和 XML 都用於承載信息.瀏覽器
JSON 是一種輕量級的數據交換格式。由道格拉斯·克羅克福特(Douglas Crockford)在 2012 年發明,並逐漸取代 XML 成爲事實上的數據交換格式標準。
JSON 基於 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。但採用徹底獨立於語言的文本格式,並使用了相似於 C 語言家族的習慣。
在 JSON 中,一共 6 種數據類型:
number:跟 Javascript 的數值一致,除去不曾使用的八進制與十六進制格式,和一些編碼細節
boolean:true
和 false
string:是由雙引號包圍的任意數量Unicode字符的集合,使用反斜線轉義
null:null
array:數組是值(value)的有序集合。一個數組以「[」(左中括號)開始,「]」(右中括號)結束,值之間使用「,」(逗號)分隔
object:對象是一個無序的「‘名稱/值’對」集合。一個對象以「{」(左括號)開始,「}」(右括號)結束,每一個「名稱」後跟一個「:」(冒號);「‘名稱/值’ 對」之間使用「,」(逗號)分隔
以及上面的任意組合。
在 JavaScript 中有一個全局對象 JSON,包含兩個方法 JSON.stringify()
和 JSON.parse()
,用於序列化和解析 JSON。
固然也有人使用 eval("(" + string + ")")
代替 JSON.parse()
來 解析JSON,相比而言這種方式的容錯性更高。
由於 XMLHttpRequest
有同源策略,而在實際開發中又經常會有跨域的需求,早期開發者爲了解決跨域問題而搞出來這樣一個頗爲奇怪的東西。產生緣由和名字同樣古怪,光聽名字恐怕沒幾我的知道說的是個什麼東西。
恰好 <script>
標籤能夠引用其餘域下的靜態資源,想一想咱們有時候在站點引入的數據統計類的 JS。
但咱們要的是數據,而不是一段靜態的 JS 代碼,怎麼辦?
這還不簡單嗎,讓服務器動態生成 js ,再把數據放進去不就能夠嗎。爲了區分每一份數據,還須要針對返回的數據作一個標識,其實就是在數據外面包裹一個函數名。
而後須要瀏覽器端預先設置好這樣一個函數,返回的數據就至關於一次執行過程,對獲取數據的處理。
AJAX 是一類技術的集合,其中最重要的是 XMLHttpRequest
JSON 是一個數據交換格式,也是目前事實上的標準
JSONP 是爲解決跨域問題搞出來的一種獲取數據的方式
接下來,用這個簡單的示例來講明如何經過 JSONP 的方式獲取數據,以及它究竟是怎樣工做的。
這裏使用 Node.js 返回一段簡單的數據。
/** * 一個簡單的 http 服務器,返回 json 數據 * 跟 Node 主頁上的那個經典例子沒太大差異 */ var http = require('http'); var urllib = require('url'); var host = '127.0.0.1'; var port = 9999; var data = {'name': 'Mirreal', 'age': '24'}; http.createServer(function(req, res) { var params = urllib.parse(req.url, true); if (params.query && params.query.callback) { var str = params.query.callback + '(' + JSON.stringify(data) + ')'; // jsonp res.writeHead(200, { 'Content-Type': 'application/javascript' }); res.end(str); } else { res.end(JSON.stringify(data)); // 普通的json } }).listen(port, host, function() { console.log('server is listening on port ' + port); });
// zepto 的寫法 $.ajax({ type: 'GET', url: 'http://127.0.0.1:9999', data: { _input_charset: 'utf-8' }, dataType: 'jsonp', timeout: 300, context: $('body'), success: function(data){ console.log(data) }, error: function(xhr, type) { console.log('Ajax error!') } });
這樣就很輕鬆的經過 JSONP 的方式獲取到數據,咱們彷佛也不須要關內心面到底是怎麼一回事。但有時候肯能會有人問起:「爲何 jsonp 不能使用 POST 方法」的問題,其實稍微瞭解一下 JSONP 的原理,這種問題徹底就不存在了。
雖然像 JQuery 這類庫將 JSONP 封裝到 $.Ajax()
上,但準確來說是不對的。由於 JSONP 只是經過動態地經過 <script>
標籤去請求一段 JS 代碼(或者叫數據),而非使用 XMLHttpRequest
,原理就像下面這樣:
/** * 對 JSONP 的一種簡單封裝 * * @param {Object} options * @returns null */ function getJsonp(options) { var callbackName = options.callbackName; var url = options.url; var scriptElem = document.createElement('script'); scriptElem.setAttribute('src', url + '?callback=' + callbackName); scriptElem.onload = function(e) { delete window[callbackName]; this.parentNode.removeChild(this); }; scriptElem.onerror = function(e) { console.log(e, 'load error'); delete window[callbackName]; this.parentNode.removeChild(this); }; window[callbackName] = options.success; // 調用 document.querySelector('head').appendChild(scriptElem); }
這段代碼對 JSONP 進行一層簡單包裝,調用也很簡單:
getJsonp({ 'url': 'http://127.0.0.1:9999/', 'callbackName': 'log', 'success': function(data) { console.log('我是回調函數,我拿到數據了', data); } });
看上去代碼還挺長的,實際上核心代碼很少,分三步:
<script>
標籤,並設置其 urlvar scriptElem = document.createElement('script'); scriptElem.setAttribute('src', url + '?callback=' + callbackName);
window[callbackName] = options.success;
這裏簡單處理,直接把傳入的回調函數設置成全局的
document.querySelector('head').appendChild(scriptElem);
實際上就是把 <script>
加到 html 文檔中,這樣就會去加載標籤的內容,也就是一個 JS 文件。
但一般現實中跑的代碼內容會更多,包含一些錯誤控制、參數拼接、超時處理、性能安全等方面的,但它仍然清楚地描述 JSONP 的原理。
早期的瀏覽器處於安全層面的考量,制定同源策略,限制了一個源(origin)中加載文本或腳本與來自其它源(origin)中資源的交互方式。
可是隨着互聯網的發展催生了跨域訪問進行數據交互的需求,因而 JSONP 就產生了,以及後來的 CORS 機制,容許 XMLHttpRequest 對象發起跨域的請求。
可是另外一方面,也增長了安全風險,咱們在使用的時候應當更加謹慎當心,防止 XSS、CSRF 等攻擊。
以前碰到一個問題,在調用一些接口返回的數據沒法使用 Chrome 預覽,本身寫測試接口的時候也碰到過。後來發現,只是由於沒有在 response 頭部加上 Content-Type: application/javascript
,僅此而已。