Ajax的技术核心是XMLHttpRequest对象(简称XHR)javascript
1 function createXHR(){ 2 if(typeof XMLHttpRequest != "undefined"){ 3 //IE7, FireFox, Opera, Chrome, Safari都支持原生的XHR对象,这些浏览器中可使用XMLHttpRequest构造函数 4 return new XMLHttpRequest(); 5 } else if (typeof ActiveXObject != "undefined"){ 6 //使用于IE7以前的版本 7 if(typeof arguments.callee.activeXString != 'string'){ 8 var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", 9 "MSXML2.XMLHttp"], 10 i, len; 11 for(i=0, len=versions.length; i<len; i++){ 12 try{ 13 new ActiveXObject(versions[i]); 14 arguments.callee.activeXString = versions[i]; 15 break; 16 } catch(ex){ 17 //跳过 18 } 19 } 20 } 21 return new ActiveXObject(arguments.callee.activeXString); 22 } else { 23 throw new Error("No XHR object available"); 24 } 25 } 26 27 //建立XHR对象 28 var xhr = createXHR();
收到响应,首先要检查status属性,通常来讲,可将http状态代码为200做为成功标志。此外,状态码304表示请求资源没有被修改,能够直接使用浏览器中缓存的版本,则响应也有效。php
对于异步请求,能够检测xhr对象的readyStatus属性,该属性表示请求/响应过程的当前活动阶段,可取值以下:html
只要readyStatus属性改变,都会触发一次readyStatechange事件。html5
1 var xhr = createXHR(); 2 //必需要在open()方法前指定onreadyStatechange事件处理程序才能确保跨浏览器兼容性 3 xhr.onreadyStatechange = function(){ 4 //这里使用xhr对象而非this对象,缘由是此事件的做用域问题 5 //若是使用this对象,在有的浏览器中会致使函数执行失败,或者致使错误发生。 6 if(xhr.readyState == 4){ 7 if((xhr.status >= 200 && xhr.status <300) || xhr.status == 304){ 8 alert(xhr.responseText); 9 } else { 10 alert("Request was unsuccessful: " + xhr.status); 11 } 12 } 13 }; 14 xhr.open("get", "example.txt", true); 15 xhr.send(null);
跨域安全策略:默认状况下, XHR对象只能访问与包含它 的页面位于同一个域中的资源。java
所谓跨域,就是由于JavaScript同源策略的限制,a.com 域名下的js没法操做b.com或是c.a.com域名下的对象。ajax
简单来讲,同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合.json
URL跨域 |
说明浏览器 |
是否容许通讯缓存 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js |
同一域名下 | 容许 |
http://www.a.com/lab/a.js http://www.a.com/script/b.js |
同一域名下不一样文件夹 | 容许 |
http://www.a.com:8000/a.js http://www.a.com/b.js |
同一域名,不一样端口 | 不容许 |
http://www.a.com/a.js https://www.a.com/b.js |
同一域名,不一样协议 | 不容许 |
http://www.a.com/a.js http://70.32.92.74/b.js |
域名和域名对应ip | 不容许 |
http://www.a.com/a.js http://script.a.com/b.js |
主域相同,子域不一样 | 不容许 |
http://www.a.com/a.js http://a.com/b.js |
同一域名,不一样二级域名(同上) | 不容许(cookie这种状况下也不容许访问) |
http://www.cnblogs.com/a.js http://www.a.com/b.js |
不一样域名 | 不容许 |
是W3C的标准。
基本思想:使用自定义的http头部让浏览器与服务器进行沟通,从而决定请求或相应是应该成功,仍是应该失败。(这一块的东西看着烦,先无论了)
即便用<img>标签。由于网页能够从任何网页中加载图像,而不用担忧是否跨域。
请求的数据是经过查询字符串形式发送的,而响应能够是任意内容。经过图像Ping,浏览器得不到任何具体的数据,可是经过侦听load和error事件,能知道响应是何时接收到的。
1 var img = new Image(); 2 //将onload和onerror事件处理程序指定为同一个函数。这样不管是什么响应,只要请求完成,就能获得通知。 3 img.onload = img.onerror = function(){ 4 alert('Done'); 5 }; 6 //请求从设置src属性那一刻开始,这里在请求中发送了一个name参数 7 img.src='http://xxxxxxxx/test?name=Nicholas';
缺点:
是被包含在函数调用中的JSON。由两部分组成:
1)回调函数:当响应到来时应该在页面中调用的函数。通常在请求中指定
2)数据:传入回调函数中的JSON数据
JSONP是经过动态<script>元素来使用,使用时能够为src属性指定一个跨域URL。这里script和img元素相似,都有能力不受限地从其余域加载资源。由于JSONP是有效的JavaScript代码,因此在请求完成后,即在JSONP响应加载到页面中之后,会当即执行。
1 function handleResponse(response){ 2 alert("You are at IP address " + response.ip + ", which is in " + 3 response.city + ", " + response.region_name); //试了下真的能够弹出个人地址!!! 4 } 5 6 var script = document.createElement("script"); 7 script.src = "http://freegeoip.net/json/?callback=handleResponse"; //指定回调函数是handleResponse() 8 document.body.insertBefore(script,document.body.firstChild);
与图片Ping相比,JSONP的优势在于:
1)可以直接访问响应文本
2)支持在浏览器与服务器之间双向通讯。
不足:
1)JSONP是从其余域中加载代码执行。其余域的安全性难以保证
2)要确保JSONP请求是否失败并不容易。<script>元素的onerror事件处理程序不被浏览器支持,所以必须使用计时器检测指定时间内是否收到响应。可是用户上网速度和带宽并不必定。
网页能够获得从其余来源动态产生的Json资料,而这种使用模式就是所谓的Jsonp。用Jsonp抓到的资料并非Json,而是任意的Javascript,用Javascript直译器执行而不是用Json解析器解析。好比:
看如下的代码就理解jsonp如何进行回调函数了。
JavaScript:
//注意:Jsonp只能使用GET请求 $.ajax({ url: 'http://www.shihj.com', dataType: 'jsonp', success: function (response) { console.log(response); } });
PHP:
$response = array('Javascript', 'PHP', 'Html', 'CSS'); $response = json_encode($response); $callback = isset($_GET['callback']) ? $_GET['callback'] : false; if ($callback) { $response = 'try{' . $callback . '(' . $response . ')}catch(e){}'; } exit($response);
Ajax是一种从页面向服务器请求数据的技术,而Comet是一种服务器向页面推送数据的技术。Comet可以让信息近乎实时地被推送到页面上。
有两种实现Comet的方式:
1)长轮询:
定义:页面发起一个到服务器的请求,而后服务器一直保持链接打开,知道有数据可发送。发送完数据后,浏览器关闭链接,随即又发起一个到服务器的新请求。这一过程在页面打开期间一直持续不断。
优势:全部浏览器都支持,使用XHR和setTimeout()就能实现
2)http流:
定义:浏览器向服务器发送一个请求,而服务器保存链接打开,而后周期性地向浏览器发送数据。
1 //在IE中不可用 2 //三个参数:url, 接收到数据时调用的函数, 关闭链接时调用的函数 3 function createStreamingClient(url, progress, finished){ 4 var xhr = new XMLHttpRequest(), 5 received = 0; 6 7 xhr.open("get", url, true); 8 xhr.onreadystatechange = function(){ 9 var result; 10 11 if(xhr.readyState == 3){ 12 13 //只取得最新数据并调整计数器 14 result = xhr.responseText.substring(received); 15 received += result.length; 16 17 //调用progress回调函数 18 progress(result); 19 } else if (xhr.readyState == 4){ 20 finished(xhr.responseText); 21 } 22 }; 23 xhr.send(null); 24 return xhr; 25 } 26 27 var client = createStreamingClient("streaming.php", function(data){ 28 alert("Received: " + data); 29 }, function(data){ 30 alert("Done!"); 31 }); 32 });
是围绕只读Comet交互推出的API或者模式。SSE API用于建立到服务器的单向链接,服务器经过这个链接能够发送任意数量的数据。
支持短轮询,长轮询和HTTP流,且能在断开链接时自动肯定什么时候从新链接
不支持ie
目标:在一个单独的持久链接上提供全双工、双向通讯。
在js中建立了WebSocket后,会有一个http请求发送到浏览器以发起链接。在取得服务器响应后,创建的链接会使用http升级从http协议交换为Web Socket协议。只有支持这种协议的服务器才能正常工做。
http:// ---> ws://
https:// ---> wss://
建立Web Socket:
var socket = new WebSocket("ws://www.example.com/server.php");
必须传入绝对url。同源策略对Web Socket不适用。所以能够链接任何站点。
实例化WebSocket对象后,浏览器就会立刻尝试建立链接。与xhr相似,WebSocket也有一个表示当前状态的readyState属性:
WebSocket.OPENING (0) | 正在创建链接 |
WebSocket.OPEN (1) | 已经创建链接 |
WebSocket.CLOSING (2) | 正在关闭链接 |
WebSocket.CLOSE (3) | 已经关闭链接 |
要关闭Web Socket链接,能够在任什么时候候调用close()方法:socket.close();
1) 是否有自由度创建和维护Web Sockets服务器?
2)是否须要双向通讯?
以上,我看Web Socket还能够实现跨域,但SSE好像没有涉及到跨域?SSE和Web Socket两节应该都是偏向于讲述两种Comet(服务器推送)的方法。而至于跨域的方法,综合上面介绍的几种:
1)图片Ping:使用<img>标签
2)JSONP:经过动态<script>元素调用
3)后台代理方法:这种方式能够解决全部跨域问题,也就是将后台做为代理,每次对其它域的请求转交给本域的后台,本域的后台经过模拟http请求去访问其它域,再将返回的结果返回给前台,这样作的好处是,不管访问的是文档,仍是js文件均可以实现跨域。
4)设置document.domain:只适用于不一样子域的框架间的交互。
5) 使用window.name:
6) 使用HTML5的新方法:window.postMessage()
window.postMessage(message, targetOrigin)
方法是html5新引进的特性,可使用它来向其它的window对象发送消息,不管这个window对象是属于同源或不一样源。
调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,若是不想限定域,可使用通配符 * 。
须要接收消息的window对象,但是经过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
上面所说的向其余window对象发送消息,其实就是指一个页面有几个框架的那种状况,由于每个框架都有一个window对象。在讨论第种方法的时候,咱们说过,不一样域的框架间是能够获取到对方的window对象的,虽然没什么用,可是有一个方法是可用的-window.postMessage
。下面看一个简单的示例,有两个页面:
a.com/index.html中的代码:
1 <iframe id="ifr" src="b.com/index.html"></iframe> 2 <script type="text/javascript"> 3 window.onload = function(){ 4 var ifr = document.getElementById('ifr'); 5 var targetOrigin = "http://b.com"; //若写成http://b.com/c/proxy.html效果同样 6 //若写成'http://c.com'就不会执行postMessage了 7 ifr.contentWindow.postMessage('I was there! ', targetOrigin); 8 }; 9 </script>
b.com/index.html中的代码:
1 <script type="text/javascript"> 2 window.addEventListener('message', function(event){ 3 //经过origin属性判断来源地址 4 if(event.origin == 'http://a.com'){ 5 alert('event.data'); //弹出I was there 6 alert(event.source); //对a.com, index.html中window对象的引用 7 //因为同源策略,这里event.source不能够访问window对象 8 } 9 },false) 10 </script>
参考: JSONP解决跨域问题