Q:AJAX以何种格式来交换数据?跨域的需求如何解决?
A:用JSON来传数据,靠JSONP来跨域(具体参见下文)
AJAX
创建对象
AJAX = Asynchronous(英[eɪˈsɪŋkrənəs]) JavaScript and XML(异步的 JavaScript 和 XML)。
XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
创建兼容对象
所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)。
// 创建 XMLHttpRequest 对象 var xmlhttp; if (window.XMLHttpRequest) {// code for IE7+,Firefox,Chrome,Opera,Safari xmlhttp=new XMLHttpRequest(); } else {// code for IE6,IE5 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } // 向服务器发送请求 // 当使用 async=true 时,请规定在响应处于 onreadystatechange 事件中的就绪状态时执行的函数 // async=false 时,请不要编写 onreadystatechange 函数 - 把代码放到 send() 语句后面即可 xmlhttp.onreadystatechange=function(){ if (xmlhttp.readyState==4 && xmlhttp.status==200){ // 来自服务器的响应 responseText:字符串形式 responseXML:XML形式 document.getElementById("myDiv").innerHTML=xmlhttp.responseText; } } xmlhttp.open("GET","test1.txt",true); xmlhttp.send();
发送请求
XMLHttpRequest 方法
-
open(method,url,async)
method:请求的类型;GET 或 POST url:文件在服务器上的地址(该文件可以是任何类型的文件或服务器脚本文件) async:true(异步)或 false(同步)
-
send(string)
string:仅用于 POST 请求
GET 还是 POST?
与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:
无法使用缓存文件(更新服务器上的文件或数据库)
向服务器发送大量数据(POST 没有数据量限制)
发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
避免得到的是缓存的结果,向URL添加一个唯一的 ID
xmlhttp.open("GET","demo_get.asp?t=" + Math.random(),true);
xmlhttp.open("GET","demo_get2.asp?fname=Bill&lname=Gates",true);
需要像 HTML 表单那样 POST 数据,请使用 setRequestHeader() 来添加 HTTP 头。然后在 send() 方法中规定您希望发送的数据
// 向请求添加 HTTP 头。 // setRequestHeader(header,value) // header: 规定头的名称 value: 规定头的值 xmlhttp.open("POST","ajax_test.asp",true); xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xmlhttp.send("fname=Bill&lname=Gates");
服务器响应
XMLHttpRequest 对象的 responseText 或 responseXML 属性
responseText:字符串形式
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
responseXML:XML形式
// 请求 books.xml 文件,并解析响应 xmlDoc=xmlhttp.responseXML; txt=""; x=xmlDoc.getElementsByTagName("ARTIST"); for (i=0;i<x.length;i++) { txt=txt + x[i].childNodes[0].nodeValue + "<br />"; } document.getElementById("myDiv").innerHTML=txt;
readyState
当请求被发送到服务器时,我们需要执行一些基于响应的任务。
readyState 属性存有 XMLHttpRequest 的状态信息。
每当 readyState 改变时,就会触发 onreadystatechange 事件。
XMLHttpRequest 对象的三个重要的属性
-
onreadystatechange
存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
-
readyState
存有 XMLHttpRequest 的状态。从0到4发生变化。(一共被触发 5 次) 0: 请求未初始化 1: 服务器连接已建立 2: 请求已接收 3: 请求处理中 4: 请求已完成,且响应已就绪
-
status
200: "OK" 404: 未找到页面
使用 Callback 函数
callback 函数是一种以参数形式传递给另一个函数的函数
JSON
JavaScript 对象表示法(JavaScript Object Notation)
是存储和交换文本信息的语法。类似 XML,比XML更小、更快,更易解析。
JSON有六种类型的值:对象,数组,字符串,数字,布尔值和特殊值null。
JSON 语法规则
数据在名称/值对中
数据由逗号分隔
花括号保存对象
方括号保存数组
JSON具有两种结构:对象,数组
对象结构以”{”大括号开始,以”}”大括号结束。中间部分由0或多个以”,”分隔的”key(关键字)/value(值)”对构成,关键字字符串和值之间以”:”分隔
{ key1:value1,key2:value2,... }
数组结构以”[”开始,”]”结束。中间由0或多个以”,”分隔的值列表组成
[ { key1:value1,... },{ key1:value1,]
JSON的解析与序列化
JSON对象(ECMAScript 5中添加的, 早期JSON解析基本都使用javascript的eval()函数。但是eval有一些性能和安全上的缺点,ECMAScript对解析JSON对象进 行了规范,定义了全局对象JSON,支持的浏览器有标准浏览器和IE8+。对于不支持的浏览器可以引入json2.js文件。)有stringify与parse这两个方法。
JSON.stringify
将javascript对象序列化为JSON格式的字符串
JSON.stringify(ob,filter,indent)包含三个参数,通常我们在使用的时候只带第一个参数,来返回字符串。
// 第二个参数是数组过滤器 var oJson = { name: 'hum',age: 20,sex: 1}; console.log(JSON.stringify(oJson,['age','sex'])); // {"age":20,"sex":1} // 第二个参数是函数过滤器 // 如果该参数是函数,则它是一个替换函数,该函数会在每一个需要字符串化的对象上调用。 // 这个函数的第一个参数是该对象中的属性名或数组的序号,第二个则是值本身。 // 函数的返回值会替换掉需要字符串化的值,如果函数返回undefined或没有任何的返回值,则会在字符串化的时候忽略这个值。 var oJson = { name: 'hum',age: 26,sex: 1,love: ['swing','jump']}; console.log(JSON.stringify(oJson,function(k,v){ switch (k){ case 'age': return v > 20 ? '成年': '未成年'; case 'love': return v.join(','); case 'sex': return undefined; default : return v; } })); // {"name":"hum","age":"成年","love":"swing,jump"}
indent:也是一个可选参数,在需要输出格式化的可阅读的代码时,使用indent参数来指定用来缩进的字符串或空格。如果省略该参数,返回的字符串将不带任何的额外的空格,这样输出的值很难阅读。就是用来格式化的。
JSON.parse
JSON.parse用来解析json格式的字符串(返回一个对象,数组或原始值)
JSON.parse(s,reviver)包含两个参数
s:要解析的字符串
reviver:用来转换解析值得可选函数
通常使用只使用第一个参数,可选参数reviver,主要是在返回解析值之前,对其进行过滤或后期处理。
reviver函数会在从s中解析的每个原始值调用一次。
调用reviver函数是带有两个参数,第一个属性名(对象的属性名或是转换成字符串的数组序号),第二个参数是对象的属性或是数组的元素值。
reviver函数会作为包含原始值的对象/数组的方法来调用。
reviver函数的返回值会成为属性的新值,如果reviver返回第二个参数,则属性不变。
如果reviver返回undefined或不凡会任何值,则会从对象或是数组中删除属性。
var oJson = { name: 'hum','jump'],birthday: '1988-01-12'}; var sJson = JSON.stringify(oJson); console.log(sJson); //{"name":"hum","age":26,"sex":1,"love":["swing","jump"],"birthday":"1988-01-12"} console.log(JSON.parse(sJson)); console.log(JSON.parse(sJson,function (k,v) { if(k == 'birthday'){ // 返回日期对象 return new Date(v); }else if(k == 'sex'){ // sex不在了 return undefined; }else{ return v; } }));
JSON格式化工具
http://www.runoob.com/jsontool
JSONP
Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。
JSONP是怎么产生的?
1、众所周知,Ajax直接请求普通文件存在跨域无权限访问的问题(原因参见 同源策略-web脚本安全)
2、但是,HTML的<script>标签是例外,可以突破同源策略从其他来源获取数据(实质是拥有"src"这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>)
3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理(现在可以使用nodejs做中间件了,本文暂且不表,改天我详细写一下~);
JSONP原理
首先在客户端注册一个callback,然后把callback的名字传给服务器。
此时,服务器先生成 json 数据。
然后以 javascript 语法的方式,生成一个function,function 名字就是传递上来的参数 jsonp.
最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)
应用代码
// 原生js 添加<script>标签的方法 //(函数名可约定,或通过地址参数传递) function addScriptTag(src){ var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } window.onload = function(){ //将自定义的回调函数名result传入callback参数中 addScriptTag("localhost/bns-relation/index.PHP?r=BnsRelation/BnsOfProd&callback=result"); } //自定义的回调函数result function result(data) { //我们就简单的获取数据 console.log(data); }
// zepto,jquery的JSONP用法 var qsData = {'searchWord':$("#searchWord").attr("value"),'currentUserId': $("#currentUserId").attr("value"),'conditionBean.pageSize':$("#pageSize").attr("value")}; $.ajax({ async:false,url: http://跨域的dns/document!searchJSONResult.action,type: "GET",dataType: 'jsonp',jsonp: 'jsoncallback',data: qsData,timeout: 5000,beforeSend: function(){ //jsonp 方式此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了 },success: function (json) {//客户端jquery预先定义好的callback函数,成功获取跨域服务器上的json数据后,会动态执行这个callback函数 if(json.actionErrors.length!=0){ alert(json.actionErrors); } genDynamicContent(qsData,type,json); },complete: function(XMLHttpRequest,textStatus){ $.unblockUI({ fadeOut: 10 }); },error: function(xhr){ //jsonp 方式此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了 //请求出错处理 alert("请求出错(请检查相关度网络状况.)"); } });
JSONP优缺点
JSONP的优点
不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;
兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;
在请求完毕后可以通过调用callback的方式回传结果。
JSONP的缺点
只支持GET请求而不支持POST等其它类型的HTTP请求;
只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
summary
ajax和jsonp其实本质上是不同的东西
ajax的核心是通过XmlHttpRequest获取非本页内容
而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本
JSON(JavaScript Object Notation)和JSONP(JSON with Padding)虽然只一个字母差别,但其实根本不是一回事儿JSON是一种数据交换格式而JSONP是一种非官方跨域数据交互协议