前言
在学习AJAX之前,有几个问题需要先花点时间弄明白
同步和异步的区别?
-
同步方法
调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。 -
异步方法
调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。所以先开始执行的代码不一定执行完才会执行下一行代码.
什么时候需要异步:
(1)需要等待的时候,等待过程不能卡在这吧
(2)等待过程不像 alert
一样阻塞程序运行
(3)等待的情况都要异步
使用异步的场景:
(1)定时任务,setTimeout
,setInterval
(2)网络请求: AJAX请求,动态<img>
加载
(3)事件绑定,点击等交互事件
JS是异步编程语言,这就是说JS代码的执行顺序并不是从上至下按部就班完成的。
为什么要使用回调函数?
先看下面的这段代码:
1
2
3
4
5
6
7
8
9
10
11
function A() {
B();
console.log('我是主函数');
}
function B(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
//调用主函数
A();
再看一个常见的回调函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
//定义主函数,回调函数作为参数
function A(callback) {
callback();
console.log('我是主函数');
}
//定义回调函数
function B(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
//调用主函数,将函数B传进去
A(B);
输出结果:
1
2
我是主函数
我是回调函数
执行的顺序是:先进入A方法 → 执行callback()函数 → 执行log函数 → 结束
同样的结果,那我们为什么要使用回调函数?
因为这样并不完美,因为这么做就相当于将B()”焊死”A()里了,如果此处我最终想调用的函数不是B()而是别的其他函数咋整?难不成要写几个不同的A1().A2().A3()?而他们之间的区别仅仅是最后调用的那个函数不同.
所以为了代码的灵活性和复用性,我们决定使用回调函数。
在大多数编程语言
中,函数的形参总是由外往内向函数体传递参数
,但在JS里如果形参是关键字”callback”则完全相反
,它表示函数体在完成某种操作后由内向外调用某个外部函数
。
补充: 如果不想为要调用的函数再单独写个方法,我们可以使用匿名函数
.那么代码则改成这样:
1
2
3
4
5
6
7
8
function A(callback){
console.log("111");
callback(123);
}
A(function(value){
console.log(value);
})
结果为:
1
2
111
123
正文
什么是AJAX?
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX 最大的优点是在不重新加载整个页面
的情况下,可以与服务器交换数据并更新部分网页内容。传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页
。
在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。
Google Suggest 使用 AJAX 创造出动态性极强的 web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。
创建XHR对象
XMLHttpRequest 对象 (异步的与服务器交换数据)
1
2
3
4
5
6
7
8
9
10
11
var xmlhttp;
if (window.XMLHttpRequest)
{
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp=new XMLHttpRequest();
}
else
{
// IE6, IE5 浏览器执行代码
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
XHR请求
如需将请求发送到服务器,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法:
1
2
xmlhttp.open("GET","ajax_info.txt",true);
xmlhttp.send();
open()
方法的三个参数:
- method:请求的类型;GET 或 POST
- url:文件在服务器上的位置
- async:true(异步)或 false(同步)
POST和GET的选择?
与 POST 相比,GET 更简单也更快
,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:
- 无法使用缓存文件(更新服务器上的文件或数据库)
- 向服务器发送大量数据(POST 没有数据量限制)
- 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
一个简单的 GET 请求:
1
2
xmlhttp.open("GET","/try/ajax/demo_get.php",true);
xmlhttp.send();
如果您希望通过 GET 方法发送信息,请向 URL 添加信息:
1
2
xmlhttp.open("GET","/try/ajax/demo_get2.php?fname=Henry&lname=Ford",true);
xmlhttp.send();
一个简单的 POST 请求:
1
2
xmlhttp.open("POST","/try/ajax/demo_post.php",true);
xmlhttp.send();
如果需要像 HTML 表单那样 POST 数据,请使用 setRequestHeader()
来添加 HTTP 头。然后在 send() 方法中规定您希望发送的数据:
1
2
3
xmlhttp.open("POST","/try/ajax/demo_post2.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); //HTTP头
xmlhttp.send("fname=Henry&lname=Ford");
setRequestHeader(header,value)
—向请求添加 HTTP 头。
- header: 规定头的名称
- value: 规定头的值
服务器响应
当我们需要执行一些基于响应的任务时,需要用到onreadystatechange()
函数.
readyState
属性存有 XMLHttpRequest
的状态信息。每当 readyState
改变时,就会触发 onreadystatechange
事件。
获取来自服务器的响应,请使用 XMLHttpRequest
对象的 responseText 或 responseXML 属性。
responseText
—获得字符串形式的响应数据。responseXML
—获得 XML 形式的响应数据。
当 readyState 等于 4 且状态为 200 时,表示响应已就绪:
1
2
3
4
5
6
7
8
xmlhttp.onreadystatechange=function()
{
//onreadystatechange 事件被触发 5 次(0 - 4),对应着 readyState 的每个变化。
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
什么是AJAX跨域?
这是因为浏览器的同源策略导致的。默认情况下,JavaScript在发送AJAX请求时,URL的域名必须和当前页面完全一致。
完全一致的意思是:
- 域名要相同(www.example.com和example.com不同)
- 协议要相同(http和https不同)
- 端口号要相同(默认是:80端口,它和:8080就不同)。有的浏览器口子松一点,允许端口不同,大多数浏览器都会严格遵守这个限制。
跨域的方法
一.通过Flash插件发送HTTP请求.
这种方式可以绕过浏览器的安全限制,但必须安装Flash,并且跟Flash交互。不过Flash用起来麻烦,而且现在用得也越来越少了。
二.通过在同源域名下架设一个代理服务器来转发.
JavaScript负责把请求发送到代理服务器.这种方式麻烦之处在于需要服务器端额外做开发。
三.JSONP
它有个限制,只能用GET请求,并且要求返回JavaScript。这种方式跨域实际上是利用了浏览器允许跨域引用JavaScript资源.
我们可以添加<script>
标签去调用外部js,再从外部js中访问外域网站,获得返回值后使用回调函数.
以163的股票查询为例,对于URL:http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice, (这是一个接口+一个回调函数) 你将得到如下返回:
1
refreshPrice({"0000001":{"code": "0000001", ... });
实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
function refreshPrice(data) {
var p = document.getElementById('test-jsonp');
p.innerHTML = '当前价格:' +
data['0000001'].name +': ' +
data['0000001'].price + ';' +
data['1399001'].name + ': ' +
data['1399001'].price;//③
}
function getPrice() {
var
js = document.createElement('script'),
head = document.getElementsByTagName('head')[0];
js.src = 'http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice';
head.appendChild(js);//②这里会先访问接口,然后把接口的返回值传给回调函数.
}
</script>
</head>
<body>
<div>
<p id="test-jsonp">当前价格:</p>
<p><button type="button" onclick="getPrice()">刷新</button></p>//①
</div>
</body>
</html>
四.CORS
CORS全称Cross-Origin Resource Sharing,是HTML5规范定义的如何跨域访问资源。
了解CORS前,我们先搞明白概念:
Origin表示本域,也就是浏览器当前页面的域。当JavaScript向外域(如sina.com)发起请求后,浏览器收到响应后,首先检查Access-Control-Allow-Origin是否包含本域,如果是,则此次跨域请求成功,如果不是,则请求失败,JavaScript将无法获取到响应的任何数据。
假设本域是my.com
,外域是sina.com
,只要响应头Access-Control-Allow-Origin
为http://my.com
,或者是*
,本次请求就可以成功。
而Access-Control-Allow-Origin
的值是由对方服务器设定的.所以,跨域能否成功,取决于对方服务器是否愿意给你设置一个正确的Access-Control-Allow-Origin
,决定权始终在对方手中。
当你引用了某个第三方CDN上的字体文件时:
1
2
3
4
@font-face {
font-family: 'FontAwesome';
src: url('http://cdn.com/fonts/fontawesome.ttf') format('truetype');
}
如果该CDN服务商未正确设置Access-Control-Allow-Origin
,那么浏览器无法加载字体资源。
这种跨域请求,称之为“简单请求”。简单请求包括GET
、HEAD
和POST
(POST的Content-Type类型 仅限application/x-www-form-urlencoded
、multipart/form-data
和text/plain
),并且不能出现任何自定义头(例如,X-Custom
: 12345),通常能满足90%的需求。
对于PUT、DELETE以及其他类型如application/json
的POST请求:
- 在发送AJAX请求之前,浏览器会先发送一个
OPTIONS
请求(称为preflighted请求)到这个URL上,询问目标服务器是否接受. - 服务器必须响应并明确指出允许的Method.
- 浏览器确认服务器响应的Access-Control-Allow-Methods头确实包含将要发送的AJAX请求的Method,才会继续发送AJAX,否则,抛出一个错误。