JavaScript笔记(AJAX 与 JSONP)
1 AJAX
Asynchronous Javascript and XML(异步传输 JavaScript和XML),简单来说就是:JS调用异步通讯组件并使用格式化的数据(包括XML,JSON)来更新web页面上的内容或操作过程,而这个方法就算是AJAX。
有很多时候,一些网站点击一个按钮提交一次HTTP请求,整个网页都会自动刷新,尽管新网页可能只有一行字不同,这是因为一个HTML请求只能对应一个页面,如果想不刷新页面发送HTTP请求,就需要JS自身发送HTTP请求,接受到数据后,再用JS更新页面内容。
1.2 编写AJAX
在现代浏览器上写AJAX主要依靠XMLHttpRequest
对象,什么是XMLHttpRequest
对象?
XMLHttpRequest
对象用于在后台与服务器交换数据,它充当着浏览器总的脚本与服务器之间的中间人角色,使用它能够做到:
- 在不重新加载页面的情况下更新网页
- 在页面已加载后从服务器请求数据
- 在页面已加载后从服务器接收数据
- 在后台向服务器发送数据
创建XMLHttpRequest
对象并不复杂,但是由于AJAX请求是异步执行的,也就是说,还要通过制作回调函数获得响应事件。
XMLHttpRequest
对象有三个重要的属性分别是:
readyState
属性:当一个XMLHttpRequest
对象初次创建时,readyState
属性为0,如果收到完整的HTTP响应,属性值就会变为4:
0
:初始化状态1
:服务器连接已建立(open()
方法已调用,但是send()
方法未调用)2
:请求已接收(Send()
方法已调用,HTTP 请求已发送到 Web 服务器)3
:请求处理中(所有响应头部都已经接收到。响应体开始接收但未完成)4
:HTTP 响应已经完全接收
status
属性:服务器返回的 HTTP 状态代码,如200
表示成功,而404
表示 “Not Found” 错误onreadystatechange
事件属性,用来存储函数,每当readyState
属性有改变,就会触发存储的函数。
仅当stats
等于200且readystate
等于4时,表示响应已就绪。
列举几个XMLHttpResquest
对象常用到方法:open()
:初始化 HTTP 请求参数,XMLHttpResquest对象方法有很多,最有用的是open
方法。它有三个参数:一指定服务器上将要访问的目标文件即URL;二指定请求类型如GET
等;三是一个布尔值,用来定义是否使用异步。setRequestHeader()
:设置头部信息,如通常的创建POST
方法,需要定义Content-Type
,也就是使用这个接口定义的了。send()
:发送 HTTP 请求,使用传递给 open() 方法的参数,以及传递给该方法的可选请求体。responseText
: 获得字符串形式的响应数据responseXML
:获得 XML 形式的响应数据
/* 创建响应时需要用到的函数 */
function success(text) {
var textarea = document.getElementById('test-response-text');
textarea.value = text;
}
/*
大概步骤分为:
使用open()指定请求目标;
确定响应函数(onreadystatechange);
使用send()发送请求;
*/
function fail(code) {
var textarea = document.getElementById('test-response-text');
textarea.value = 'Error code: ' + code;
}
var request = new XMLHttpRequest(); // 新建XMLHttpRequest对象
request.onreadystatechange = function () { // 状态发生变化时,函数被回调,注意参数是函数,而不是直接调用函数
if (request.readyState === 4) { // 成功完成
// 判断响应结果:
if (request.status === 200) {
// 成功,通过responseText拿到响应的文本:
return success(request.responseText);
} else {
// 失败,根据响应码判断失败原因:
return fail(request.status);
}
} else {
// HTTP请求还在继续...
}
}
// 发送请求:
request.open('POST', '/api/categories');
request.setRequsetHeader("Content-Type","application/json"//"application/x-www-form-urlencoded"
var data = JSON.stringify(data);
request.send(data);
alert('请求已发送,请等待响应...');
对于低版本的IE,需要换一个ActiveXObject
对象,用法都一样,通过检测window对象是否有XMLHttpRequest
属性来确定浏览器是否支持标准的XMLHttpRequest
,eg:
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else {
request = new ActiveXObject('Microsoft.XMLHTTP');
}
- 因为实现异步,所以如果其他脚本都依赖服务器的响应,那么就该把相应的代码转移到指定
onreadystatechange
属性的那个函数上来。- 使用AJAX,要注意同源策略,也就是XMLHttpResquest对象发送的请求只能访问与其所在的HTML文件处于同一域的数据,即对于AJAX的
open()
响应url是相对路径,不能是绝对路径,即无法请求外域(就是其他网站)的URL。
另外,在拼接URL的时候还可能会使用encodeURIComponent()
函数,encodeURIComponent()
函数可把字符串作为 URI 组件进行编码,如:url = encodeURIComponent(a)
2 JSONP
参考网站:
JSONP讲解
AJAX直接请求普通文件存在跨域无权限访问的问题,不过Web页面上调用外部引入的js文件时则不受是否跨域的影响(拥有src
这个属性的标签都拥有跨域的能力,比如<script>
、<img>
、<iframe>
)。
web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。
<html>
<head>
<script>
function refreshPrice(data) {
var p = document.getElementById('test-jsonp');
p.innerHTML = '当前价格:' +
data['0000001'].name +':' +
data['0000001'].price + ';' +
data['1399001'].name + ':' +
data['1399001'].price;
}
</script>
<script>
function getPrice() {
var js = document.createElement('script');
var head = document.getElementsByTagName('head')[0];
js.src = 'http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice';
head.appendChild(js);
}
</script>
</head>
<body>
<p id='test-jsonp'>当前价格:</p>
<button type="button" onclick="getPrice()">刷新</button>
</body>
</html>
讲解:当点击刷新按钮之后,会自动执行getPrice()
函数,getPrice()
函数的作用是在HTML文档中动态生成一个<script>
脚本,该脚本调用了http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice
这个URL,调用的URL中调用了一个callback
参数,这个callback
参数的作用是告诉浏览器,该URL的本地回调函数叫做refreshPrice
,所以请把查询结果传入这个函数中进行调用。也就是说这时会自动执行refreshPrice
函数,并把服务器本身生成的json传进作为参数。
3 CORS跨域策略
参考文章:《跨域资源共享 CORS 详解》
CORS跨域策略,全称Cross-Origin Resource Sharing(HTML5),它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持,目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
4 设置document.domain
跨域
比如,有一个页面,它的地址是http://www.example.com/a.html
, 在这个页面里面有一个iframe,它的src
是http://example.com/b.html
, 很显然,这个页面与它里面的iframe
框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe
中的东西的,这时可以使用设置document.domain
- 在页面
http://www.example.com/a.html
中设置document.domain
,如
<iframe src="http://example.com/b.html" id="iframe" onload='test()'></iframe>
<script>
document.domain = 'example.com'
function test () {
alert(document.getElementById('iframe').contentWindow)
}
</script>
在页面http://example.com/b.html
中也设置document.domain
,而且这也是必须的,虽然这个文档的domain
就是example.com
,但是还是必须显示的设置document.domain
的值:
<script>
document.domain = 'example.com'//重复定义
</script>
5 window.postMessage
(HTML5)
window.postMessage(message,targetOrigin)
方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、 FireFox、Chrome、Opera等浏览器都已经支持window.postMessage
方法。
调用postMessage
方法的window
对象是指要接收消息的那一个window
对象,该方法的第一个参数message
为要发送的消息,类 型只能为字符串;第二个参数targetOrigin
用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符*
。
需要接收消息的window
对象,可是通过监听自身的message
事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
<!-- a.html页面 -->
<script>
function onload () {
var iframe = document.getElementById('iframe');
var win = iframe.contentWindow();
win.postMessage('来自a页面的消息','*');
}
</script>
<iframe src="http://example.com/b.html" id="iframe" onload='onload()'></iframe>
b.html页面
window.onmessage = funcation (e) { //onmessage事件
e = e || event;
alert(e.data) // data属性达到传送的消息
}
当然还有好几种使用外域的方法,比如
- 通过在同源域名下架设一个代理服务器来转发,JavaScript负责把请求发送到代理服务器
- 通过Flash插件发送HTTP请求,这种方式可以绕过浏览器的安全限制,但必须安装Flash,并且跟Flash交互。不过Flash用起来麻烦,而且现在用得也越来越少了
4 总结
Ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XMLHttpRequest
对象获取非本页内容,而jsonp的核心则是动态添加<script>
标签来调用服务器提供的js脚本,另外jsonp是一种方式或者说非强制性协议,如同Ajax一样,它也不一定非要用json格式来传递数据。