AJAX、JSON燃异、JSONP
在 WEB 開發(fā)中携狭,經(jīng)常見到諸如 AJAX、JSON回俐、JSONP 這些詞逛腿,但這三種東西到底是什么,又有什么關(guān)系和區(qū)別仅颇。
AJAX (Asynchronous JavaScript + XML)
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;
- dynamic display and interaction using the Document Object Model;
- data interchange and manipulation using XML and XSLT;
- asynchronous data retrieval using XMLHttpRequest;
- and JavaScript binding everything together.
異步 JavaScript + XML, 本身不是一種技術(shù), 是在2005年由 Jesse James Garrett 提出的一個術(shù)語, 描述了一種結(jié)合使用大量已有技術(shù)的方式, 包括: HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 還有最重要的 XMLHttpRequest 對象.
盡管在 AJAX 中 X 代表 XML, 但現(xiàn)在 JSON 使用的更多单默,因為 JSON 具有很多優(yōu)勢,比如更輕量并且是 JavaScript 的一部分. 在 AJAX 模型中 JSON 和 XML 都用于承載信息.
JSON(Javascript Object Notation)
JSON 是一種輕量級的數(shù)據(jù)交換格式忘瓦。由道格拉斯·克羅克福特(Douglas Crockford)在 2012 年發(fā)明搁廓,并逐漸取代 XML 成為事實上的數(shù)據(jù)交換格式標準。
JSON 基于 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集耕皮。但采用完全獨立于語言的文本格式境蜕,并使用了類似于 C 語言家族的習慣。
在 JSON 中凌停,一共 6 種數(shù)據(jù)類型:
- number:跟 Javascript 的數(shù)值一致粱年,除去未曾使用的八進制與十六進制格式,和一些編碼細節(jié)
- boolean:
true
和false
- string:是由雙引號包圍的任意數(shù)量Unicode字符的集合罚拟,使用反斜線轉(zhuǎn)義
- null:
null
- array:數(shù)組是值(value)的有序集合台诗。一個數(shù)組以“[”(左中括號)開始完箩,“]”(右中括號)結(jié)束,值之間使用“,”(逗號)分隔
- object:對象是一個無序的“‘名稱/值’對”集合拉庶。一個對象以“{”(左括號)開始嗜憔,“}”(右括號)結(jié)束秃励,每個“名稱”后跟一個“:”(冒號)氏仗;“‘名稱/值’ 對”之間使用“,”(逗號)分隔
以及上面的任意組合。
在 javascript 中有一個全局的對象 JSON夺鲜,包含兩個方法 JSON.stringify()
和 JSON.parse()
皆尔,用于序列化和解析 JSON。
JSONP(JSON with Padding)
最初是開發(fā)者為了解決跨域問題搞出來的一個頗為奇怪的東西币励,產(chǎn)生原因和名字一樣古怪慷蠕,光聽名字恐怕沒幾個人知道說的是個什么東西。
因為 ajax 請求收到同源策略的限制不允許跨域訪問食呻,而在實際開發(fā)中又常常會有類似的需求流炕。
剛好 <script>
標簽可以引用其他站點的靜態(tài)資源,想想我們有時候在站點引入的數(shù)據(jù)統(tǒng)計類的 js仅胞。
但我們要的是數(shù)據(jù)每辟,而不是一段靜態(tài)代碼,怎么辦干旧?
這還不簡單嗎渠欺,讓服務器動態(tài)生成 js ,再把數(shù)據(jù)放進去不就可以嗎椎眯。為了區(qū)分不數(shù)據(jù)挠将,還需要針對返回的數(shù)據(jù)做一個標識,其實就是在數(shù)據(jù)外面包裹一個函數(shù)名编整。
然后需要瀏覽器端預先設(shè)置好這樣一個函數(shù)舔稀,返回的數(shù)據(jù)就相當于一次執(zhí)行過程,對獲取數(shù)據(jù)的處理掌测。
總結(jié)
- AJAX 是一類技術(shù)的集合内贮,其中最重要的是
XMLHttpRequest
- JSON 是一個數(shù)據(jù)交換格式
- JSONP 是為了解決跨域問題搞出來的一種獲取數(shù)據(jù)的方式
下面舉個栗子
服務器
這里使用 node 返回一段簡單的數(shù)據(jù)。
/**
* 一個簡單的 http 服務器,返回 json 數(shù)據(jù)
* 跟 node 主頁上的那個經(jīng)典例子沒太大差別
*/
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 的方式獲取到數(shù)據(jù)赏半,我們也不需要關(guān)心里面究竟是怎么一回事贺归,但經(jīng)常會有人問起像“為什么 jsonp 不能使用 POST 方法”的問題,其實稍微了解一下 JSONP 的原理断箫,這種問題完全就不存在了拂酣。
雖然像 jQuery 這類庫將 jsonp 封裝到 Ajax 上,但準確來講是不對的仲义。因為 jsonp 只是通過動態(tài)地通過 <script>
標簽去請求一段 js 代碼(或者叫數(shù)據(jù))婶熬,而非使用 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;
// 調(diào)用
document.querySelector('head').appendChild(scriptElem);
}
這段代碼對 JSONP 進行一層簡單包裝,調(diào)用也很簡單:
getJsonp({
'url': 'http://127.0.0.1:9999/',
'callbackName': 'log',
'success': function (data) {
console.log('我是回調(diào)函數(shù),我拿到數(shù)據(jù)了', data);
}
});
看上去代碼還挺長的赵颅,實際上核心代碼不多虽另,分三步:
1.創(chuàng)建一個 <script>
標簽,并設(shè)置其 url
var scriptElem = document.createElement('script');
scriptElem.setAttribute('src', url + '?callback=' + callbackName);
2.設(shè)置回調(diào)函數(shù)
window[callbackName] = options.success;
這里簡單處理饺谬,直接把傳入的回調(diào)函數(shù)設(shè)置成全局的
3.調(diào)用
document.querySelector('head').appendChild(scriptElem);
實際上就是把 <script>
加到 html 文檔中捂刺,這樣就會去加載標簽的內(nèi)容,也就是一個 js 文件募寨。
但通匙逭梗現(xiàn)實中跑的代碼內(nèi)容會更多,包含一些錯誤控制拔鹰、參數(shù)拼接仪缸、超時處理而克、性能安全等方面的礁击,但它仍然清楚地描述 JSONP 的原理。
安全
早期的瀏覽器處于安全層面的考量生兆,使用同源策略瓷马,限制了一個源(origin)中加載文本或腳本與來自其它源(origin)中資源的交互方式拴还。
但是隨著互聯(lián)網(wǎng)的發(fā)展催生了跨域訪問進行數(shù)據(jù)交互的需求,于是 JSONP 就產(chǎn)生了决采,以及后來的 CORS 機制自沧,允許 XMLHttpRequest 對象發(fā)起跨域的請求。
但是另一方面树瞭,也增加了安全風險拇厢,我們在使用的時候應當更加謹慎小心,防止 XSS晒喷、CSRF 等攻擊孝偎。
其他
數(shù)據(jù)預覽
之前碰到一個問題,為什么調(diào)用一些接口返回的數(shù)據(jù)無法使用 Chrome 預覽凉敲,我自己寫測試接口的時候也碰到過衣盾。其實這里完全沒有什么技術(shù)可言,只是因為沒有在 response 頭部加上 Content-Type: application/javascript
爷抓,僅此而已势决。
參考文檔
- AJAX | MDN
- Jesse James Garrett 的文章:Ajax: A New Approach to Web Applications
- JSON