一、JSON
JSON
是一种数据格式,与XML
相比,JSON
是在JavaScript
中读写结构化数据的更好的方式。因为可以把JSON
直接传给eval()
,而且不必创建DOM
对象。
-
JSON
对象没有声明变量(JSON
中没有变量的概念) - 没有末尾的分号,除最后一项外,每项末尾都需要有逗号
- 对象的属性必须加双引
- 属性的值可以是简单值,也可以是复杂类型值。
- 能使用下标、方括号、点的方式直接访问
JSON
对象中的属性值
1.1 JSON 解析与序列化
JSON.stringify()
把一个JavaScript
对象序列化为一个JSON
字符串然后返回,默认情况下,JSON.stringify()
输出的JSON 字符串不包含任何空格字符或缩进。
在序列化JavaScript
对象时,所有函数及原型成员都会被有意忽略,不体现在结果中。此外,值为undefined
的任何属性也都会被跳过。结果中最终都是值为有效JSON
数据类型的实例属性。
还可以接收另外两个参数,第一个参数是个过滤器,可以是一个数组,也可以是一个函数;第二个参数是一个选项,表示是否在JSON
字符串中保留缩进。
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
"edition": 3,
"year": 2011
};
var jsonText = JSON.stringify(book, ["title", "edition"]);
// 在返回的结果字符串中,就只会包含这两个属性:"title", "edition"
第二个参数是函数,行为会稍有不同。
var jsonText = JSON.stringify(book, function(key, value){
switch(key){
case "authors":
return value.join(",")
case "year":
return 5000;
case "edition":
return undefined; // 通过返回undefined 删除该属性。
default:
return value;
}
});
// {"title":"Professional JavaScript","authors":"Nicholas C. Zakas","year":5000}
第三个参数用于控制结果中的缩进和空白符。如果这个参数是一个数值,那它表示的是每个级别缩进的空格数。只要传入有效的控制缩进的参数值,结果字符串就会包含换行符。(只缩进而不换行意义不大。)最大缩进空格数为10
,所有大于10
的值都会自动转换为10
。在使用字符串的情况下,可以将缩进字符设置为制表符,或者两个短划线之类的任意字符
var jsonText = JSON.stringify(book, null, " - -");
{
--"title": "Professional JavaScript",
--"authors": [
----"Nicholas C. Zakas"
--],
--"edition": 3,
--"year": 2011
}
// 缩进字符串最长不能超过10 个字符长。如果字符串长度超过了10 个,结果中将只出现前10 个字符。
toJSON()
有时候,JSON.stringify()
还是不能满足对某些对象进行自定义序列化的需求。在这些情况下,可以给对象定义toJSON()
方法,在JSON.stringify()
时返回其自定义数据格式。
序列化JSON
对象的顺序如下:
(1) 如果存在toJSON()
方法而且能通过它取得有效的值,则调用该方法。否则,返回对象本身。
(2) 如果提供了第二个参数,应用这个函数过滤器。传入函数过滤器的值是第(1)步返回的值。
(3) 对第(2)步返回的每个值进行相应的序列化。
(4) 如果提供了第三个参数,执行相应的格式化。
JSON.parse()
将JSON
字符串直接传递给JSON.parse()
就可以得到相应的JavaScript
值,JSON.parse()
方法也可以接收另一个参数,该参数是一个函数,将在每个键值对上调用。这个函数接收两个参数,一个键和一个值,而且都需要返回一个值。
var book = {releaseDate: new Date(2011, 11, 1)};
var jsonText = JSON.stringify(book);
var bookCopy = JSON.parse(jsonText, function(key, value){
if (key == "releaseDate"){
return new Date(value);
} else {
return value;
}
})
alert(bookCopy.releaseDate.getFullYear());
// 最后解析出来的还是一个Date对象
二、Ajax
Ajax
技术的核心是XMLHttpRequest
对象(简称XHR
),这是由微软首先引入的一个特性,其他浏览器提供商后来都提供了相同的实现。XHR
为向服务器发送请求和解析服务器响应提供了流畅的接口。能够以异步方式从服务器取得更多信息,意味着用户单击后,可以不必刷新页面也能取得新据。也就是说,可以使用XHR
对象取得新数据,然后再通过DOM
将新数据插入到页面中。
使用
在使用XHR 对象时,要调用的第一个方法是open()
,它接受3 个参数:要发送的请求的类型("get"
、"post"
等)、请求的URL
和表示是否异步发送请求的布尔值。
var xhr = new XMLHttpRequest();
xhr.open("get", "example.php", false);
xhr.send(null);
-
URL
相对于执行代码的当前页面(当然也可以使用绝对路径); - 调用
open()
方法并不会真正发送请求,而只是启动一个请求以备发送。要发送特定的请求,必须调用send()
方法,这里的send()
方法接收一个参数,即要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入null
,因为这个参数对有些浏览器来说是必需的。调用send()
之后,请求就会被分派到服务器。 - 第三个参数
false
代表请求是同步的,JavaScript
代码会等到服务器响应之后再继续执行,默认为true
。
服务器响应的数据会自动填充XHR
对象的属性,相关的属性简介如下。
-
responseText
:作为响应主体被返回的文本。 -
responseXML
:如果响应的内容类型是"text/xml"
或"application/xml"
,这个属性中将保存包含着响应数据的XML DOM
文档。 -
status
:响应的HTTP
状态。 -
statusText
:HTTP
状态的说明。
但多数情况下,我们还是要发送异步请求,才能让JavaScript
继续执行而不必等待响应。此时,可以检测XHR
对象的readyState
属性,该属性表示请求/响应过程的当前活动阶段。
- 0:未初始化。尚未调用
open()
方法。 - 1:启动。已经调用
open()
方法,但尚未调用send()
方法。 - 2:发送。已经调用
send()
方法,但尚未接收到响应。 - 3:接收。已经接收到部分响应数据。
- 4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。
只要readyState
属性的值由一个值变成另一个值,都会触发一次readystatechange
事件。可以利用这个事件来检测每次状态变化后readyState
的值。通常,我们只对readyState
值为4
的阶段感兴趣,因为这时所有数据都已经就绪。不过,必须在调用open()
之前指定onreadystatechange
事件处理程序才能确保跨浏览器兼容性。
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("get", "example.txt", true);
xhr.send(null);
另外,在接收到响应之前还可以调用abort()
方法来取消异步请求,如下所示:
xhr.abort();
调用这个方法后,XHR
对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性。在终止请求之后,还应该对XHR
对象进行解引用操作。由于内存原因,不建议重用XHR
对象。
HTTP头部信息
每个HTTP
请求和响应都会带有相应的头部信息,其中有的对开发人员有用,有的也没有什么用。XHR
对象也提供了操作这两种头部(即请求头部和响应头部)信息的方法。
默认情况下,在发送XHR
请求的同时,还会发送下列头部信息。
-
Accept
:浏览器能够处理的内容类型。 -
Accept-Charset
:浏览器能够显示的字符集。 -
Accept-Encoding
:浏览器能够处理的压缩编码。 -
Accept-Language
:浏览器当前设置的语言。 -
Connection
:浏览器与服务器之间连接的类型。 -
Cookie
:当前页面设置的任何Cookie
。 -
Host
:发出请求的页面所在的域 。 -
Referer
:发出请求的页面的URI
。 -
User-Agent
:浏览器的用户代理字符串。
setRequestHeader()
方法可以设置自定义的请求头部信息。这个方法接受两个参数:头部字段的名称和头部字段的值。
要成功发送请求头部信息,必须在调用open()
方法之后且调用send()
方法之前调用setRequestHeader()
。
xhr.open("get", "example.php", true);
xhr.setRequestHeader("MyHeader", "MyValue");
xhr.send(null);
- 响应头信息
调用XHR
对象的getResponseHeader()
方法并传入头部字段名称,可以取得相应的响应头部信息。而调用getAllResponseHeaders()
方法则可以取得一个包含所有头部信息的长字符串。
GET请求
GET
是最常见的请求类型,最常用于向服务器查询某些信息。必要时,可以将查询字符串参数追加到URL
的末尾,以便将信息发送给服务器。对XHR
而言,位于传入open()
方法的URL
末尾的查询字符串必须使用encodeURIComponent()
进行编码。
function addURLParam(url, name, value) {
url += (url.indexOf("?") == -1 ? "?" : "&");// 没有?加? 有问号加&
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
var url = "example.php";
//添加参数
url = addURLParam(url, "name", "Nicholas");
url = addURLParam(url, "book", "Professional JavaScript");
//初始化请求
xhr.open("get", url, false);
POST请求
POST
请求,通常用于向服务器发送应该被保存的数据。POST
请求应该把数据作为请求的主体提交,而GET
请求传统上不是这样。POST
请求的主体可以包含非常多的数据,而且格式不限。
我们可以使用XHR 来模仿表单提交:首先将Content-Type
头部信息设置为application/x-www-form-urlencoded
,也就是表单提交时的内容类型,其次是以适当的格式创建一个字符串。
xhr.open("post", "postexample.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var form = document.getElementById("user-info");
xhr.send(serialize(form)); // serialize(form) 自定义方法 用来序列化表单
postexample.php
就可以通过$_POST
取得提交的数据了。如果不设置Content-Type
头部信息,那么发送给服务器的数据就不会出现在$_POST
超级全局变量中。这时候,要访问同样的数据,就必须借助$HTTP_RAW_POST_DATA
。
XMLHttpRequest 2 级
-
FormData
对象
FormData
为序列化表单以及创建与表单格式相同的数据(用于通过XHR
传输)提供了便利
var data = new FormData();
data.append("name", "Nicholas");
append()
方法接收两个参数:键和值,分别对应表单字段的名字和字段中包含的值。可以像这样添加任意多个键值对。
而通过向FormData
构造函数中传入表单元素,也可以用表单元素的数据预先向其中填入键值对。
var data = new FormData(document.forms[0]);
// 也可以将它直接传给XHR 的send()方法,
xhr.open("post","postexample.php", true);
xhr.send(new FormData(document.forms[0]));
- 超时设定
IE8
为XHR
对象添加了一个timeout
属性,表示请求在等待响应多少毫秒之后就终止。在给timeout
设置一个数值后,如果在规定的时间内浏览器还没有接收到响应,那么就会触发timeout
事件,进而会调用ontimeout
事件处理程序。这项功能后来也被收入了XMLHttpRequest 2
级规范中。
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
try {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
} catch (ex){
//假设由ontimeout 事件处理程序处理
}
}
};
xhr.open("get", "timeout.php", true);
xhr.timeout = 1000; //将超时设置为1 秒钟(仅适用于IE8+)
xhr.ontimeout = function(){
alert("Request did not return in a second.");
};
xhr.send(null);
超时响应导致请求终止时,会调用ontimeout
事件处理程序。但此时readyStat
可能已经改变为4
了,这意味着会调用onreadystatechange
事件处理程序。可是,如果在超时终止请求之后再访问status
属性,就会导致错误。为避免浏览器报告错误,可以将检查status
属性的语句封装在一个try-catch
语句当中。
-
overrideMimeType()
方法
如果服务器返回的MIME
类型是text/plain
,但数据中实际包含的是XML
。根据MIME
类型,即使数据是XML
,responseXML
属性中仍然是null
。通过调用overrideMimeType()
方法,可以保证把响应当作XML
而非纯文本来处理。
调用overrideMimeType()
必须在send()
方法之前,才能保证重写响应的MIME
类型。
xhr.open("get", "text.php", true);
xhr.overrideMimeType("text/xml");
xhr.send(null);
进度事件
-
loadstart
:在接收到响应数据的第一个字节时触发。 -
progress
:在接收响应期间持续不断地触发。 -
error
:在请求发生错误时触发。 -
abort
:在因为调用abort()方法而终止连接时触发。 -
load
:在接收到完整的响应数据时触发。 -
loadend
:在通信完成或者触发error
、abort
或load
事件后触发。
每个请求都从触发loadstart
事件开始,接下来是一或多个progress
事件,然后触发error
、abort
或load
事件中的一个,最后以触发loadend
事件结束。
-
load
事件
用以替代readystatechange
事件,响应接收完毕后将触发load
事件,因此也就没有必要去检查readyState
属性了。
而onload
事件处理程序会接收到一个event
对象,其target
属性就指向XHR
对象实例,因而可以访问到XHR
对象的所有方法和属性。然而,并非所有浏览器都为这个事件实现了适当的事件对象。所以建议依然使用xhr.status
而非e.target.status
只要浏览器接收到服务器的响应,不管其状态如何,都会触发load
事件。而这意味着你必须要检查status
属性,才能确定数据是否真的已经可用了
xhr.onload = function(){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);
-
progress
事件
这个事件会在浏览器接收新数据期间周期性地触发。而onprogress
事件处理程序会接收到一个event
对象,其target
属性是XHR 对象,但包含着三个额外的属性:
-
lengthComputable
:表示进度信息是否可用的布尔值。 -
position
:表示已经接收的字节数byte
。 -
totalSize
:表示根据Content-Length
响应头部确定的预期字节数byte
。
为确保正常执行,必须在调用open()
方法之前添加onprogress
事件处理程序。
xhr.onprogress = function(event){
var divStatus = document.getElementById("status");
if (event.lengthComputable){
divStatus.innerHTML = "Received " + event.position + " of " +
event.totalSize +" bytes";
}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);
跨域
CORS
(Cross-Origin Resource Sharing
,跨源资源共享)是W3C
的一个工作草案,定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通。CORS
背后的基本思想,就是使用自定义的HTTP
头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。
在发送请求时,需要给它附加一个额外的Origin
头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。
Origin: http://www.nczonline.net
如果服务器认为这个请求可以接受,就在Access-Control-Allow-Origin
头部中回发相同的源信息(如果是公共资源,可以回发"*"
)。
Access-Control-Allow-Origin: http://www.nczonline.net
如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器会处理请求。注意,请求和响应都不包含cookie
信息。
IE
对CORS
的实现
微软在IE8
中引入了XDR
(XDomainRequest
)类型。这个对象与XHR
类似,但能实现安全可靠的跨域通信。XDR
对象的安全机制部分实现了W3C
的CORS
规范。
以下是XDR
与XHR
的一些不同之处。
-
cookie
不会随请求发送,也不会随响应返回。 - 只能设置请求头部信息中的
Content-Type
字段。 - 不能访问响应头部信息。
- 只支持
GET
和POST
请求
这些变化使CSRF
(Cross-Site Request Forgery
,跨站点请求伪造)和XSS
(Cross-Site Scripting
,跨站点脚本)的问题得到了缓解。被请求的资源可以根据它认为合适的任意数据(用户代理、来源页面等)来决定是否设置Access-Control- Allow-Origin
头部。作为请求的一部分,Origin
头部的值表示请求的来源域,以便远程资源明确地识别XDR
请求。
使用也与XHR
对象非常相似,也是创建一个XDomainRequest
的实例,调用open()
方法,再调用send()
方法。XDR
对象的open()
方法只接收两个参数:请求的类型和URL
。所有XDR
请求都是异步执行的,不能用它来创建同步请求。请求返回之后,会触发load
事件,响应的数据也会保存在responseText
属性中 。
var xdr = new XDomainRequest();
xdr.onload = function(){
alert(xdr.responseText);
};
xdr.onerror = function(){
alert("An error occurred.");
};
xdr.open("get", "http://www.somewhere-else.com/page/");
xdr.send(null);
如果失败(包括响应中缺少Access-Control-Allow-Origin
头部)就会触发error
事件。遗憾的是,除了错误本身之外,没有其他信息可用,因此唯一能够确定的就只有请求未成功了。
也可以在请求返回前调用xdr.abort();
来终止请求,也支持timeout
属性以及ontimeout
事件处理程序。
为支持POST
请求,XDR
对象提供了contentType
属性,用来表示发送数据的格式。
xdr.open("post", "http://www.somewhere-else.com/page/");
xdr.contentType = "application/x-www-form-urlencoded";
xdr.send("name1=value1&name2=value2");
其他浏览器对CORS
的实现
其他对象都通过XMLHttpRequest
对象实现了对CORS
的原生支持。在尝试打开不同来源的资源时,无需额外编写代码就可以触发这个行为。要请求位于另一个域中的资源,使用标准的XHR
对象并在open()
方法中传入绝对URL
即可。
通过跨域XHR
对象可以访问status
和statusText
属性,而且还支持同步请求。跨域XHR
对象也有一些限制,但为了安全这些限制是必需的。以下就是这些限制。
- 不能使用
setRequestHeader()
设置自定义头部。 - 不能发送和接收
cookie
。 - 调用
getAllResponseHeaders()
方法总会返回空字符串。
由于无论同源请求还是跨源请求都使用相同的接口,因此对于本地资源,最好使用相对URL
,在访问远程资源时再使用绝对URL
。这样做能消除歧义,避免出现限制访问头部或本地cookie
信息等问题。
Preflighted Reqeusts
预检请求
CORS
通过一种叫做Preflighted Requests
的透明服务器验证机制支持开发人员使用自定义的头部、使用GET
与POST
之外的方法,以及使用不同类型的主体内容。
在使用下列高级选项来发送请求时,就会向服务器发送一个Preflight
请求。这种请求使用OPTIONS
方法,发送下列头部。
-
Origin
:与简单的请求相同。 -
Access-Control-Request-Method
:请求自身使用的方法。 -
Access-Control-Request-Headers
:(可选)自定义的头部信息,多个头部以逗号分隔。
Origin: http://www.nczonline.net
Access-Control-Request-Method: POST
Access-Control-Request-Headers: myHeaders
发送这个请求后,服务器可以决定是否允许这种类型的请求。服务器通过在响应中发送如下头部与浏览器进行沟通。
-
Access-Control-Allow-Origin
:与简单的请求相同。 -
Access-Control-Allow-Methods
:允许的方法,多个方法以逗号分隔。 -
Access-Control-Allow-Headers
:允许的头部,多个头部以逗号分隔。 -
Access-Control-Max-Age
:应该将这个Preflight
请求缓存多长时间(以秒表示)。
Access-Control-Allow-Origin: http://www.nczonline.net
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Headers: NCZ
Access-Control-Max-Age: 1728000
Preflight
请求结束后,结果将按照响应中指定的时间缓存起来。而为此付出的代价只是第一次发送这种请求时会多一次HTTP
请求。
带凭据的请求
默认情况下,跨源请求不提供凭据(cookie
、HTTP
认证及客户端SSL
证明等)。通过将withCredentials
属性设置为true
,可以指定某个请求应该发送凭据。如果服务器接受带凭据的请求,会用下面的HTTP
头部来响应。
Access-Control-Allow-Credentials: true
如果发送的是带凭据的请求,但服务器的响应中没有包含这个头部,那么responseText
中将是空字符串,status
的值为0
,而且会调用onerror()
事件处理程序。另外,服务器还可以在Preflight
响应中发送这个HTTP
头部,表示允许源发送带凭据的请求。
其他跨域技术
1、使用标签
一个网页可以从任何网页中加载图像,不用担心跨域不跨域。图像Ping
是与服务器进行简单、单向的跨域通信的一种方式。
图像Ping
有两个主要的缺点,一是只能发送GET
请求,二是无法访问服务器的响应文本。因此,图像Ping
只能用于浏览器与服务器间的单向通信。
请求的数据是通过查询字符串形式发送的,而响应可以是任意内容,但通常是像素图或204
响应。通过图像Ping
,浏览器得不到任何具体的数据,但通过侦听load
和error
事件,它能知道响应是什么时候接收到的。
var img = new Image();
img.onload = img.onerror = function(){
alert("Done!");
};
// 为什么指定为同一个函数 因为只能知道响应是什么时候接收到的。
// 无论是什么响应,只要请求完成,就能得到通知
img.src = "http://www.example.com/test?name=Nicholas";
// 请求从设置src 属性那一刻开始
2、JSONP
JSONP
是JSON with padding
(填充式JSON
或参数式JSON
)的简写,是应用JSON
的一种新方法。JSONP
看起来与JSON
差不多,只不过是被包含在函数调用中的JSON
。例如:callback({ "name": "Nicholas" });
JSONP
由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的JSON
数据。
JSONP
是通过动态