高程笔记7
语法
JSON 的语法可以表示以下三种类型的值。 简单值:使用与 JavaScript 相同的语法,可以在 JSON 中表示字符串、数值、布尔值和 null。 但 JSON 不支持 JavaScript 中的特殊值 undefined。 对象:对象作为一种复杂数据类型,表示的是一组无序的键值对儿。而每个键值对儿中的值可 以是简单值,也可以是复杂数据类型的值。 数组:数组也是一种复杂数据类型,表示一组有序的值的列表,可以通过数值索引来访问其中 的值。数组的值也可以是任意类型——简单值、对象或数组。 JSON 不支持变量、函数或对象实例,它就是一种表示结构化数据的格式,虽然与 JavaScript 中表示 数据的某些语法相同,但它并不局限于 JavaScript 的范畴。
对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 JSON 中的对象与 JavaScript 字面量稍微有一些不同。下面是一个 JavaScript 中的对象字面量: var person = { name: "Nicholas", age: 29 }; 这虽然是开发人员在 JavaScript 中创建对象字面量的标准方式,但 JSON 中的对象要求给属性加引 号。实际上,在 JavaScript 中,前面的对象字面量完全可以写成下面这样: var object = { "name": "Nicholas", "age": 29 }; JSON 表示上述对象的方式如下: { "name": "Nicholas", "age": 29 }
JSON 对象有两个方法:stringify()和 parse()。在最简单的情况下,这两个方法分别用于把 JavaScript 对象序列化为 JSON 字符串和把 JSON 字符串解析为原生 JavaScript 值。例如:
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 var book = { title: "Professional JavaScript", authors: [ "Nicholas C. Zakas" ], edition: 3, year: 2011 }; var jsonText = JSON.stringify(book); 实际上,JSON.stringify()除了要序列化的 JavaScript 对象外,还可以接收另外两个参数,这两 个参数用于指定以不同的方式序列化 JavaScript 对象。第一个参数是个过滤器,可以是一个数组,也可 以是一个函数;第二个参数是一个选项,表示是否在 JSON 字符串中保留缩进。单独或组合使用这两个 参数,可以更全面深入地控制 JSON 的序列化 2. 字符串缩进 JSON.stringify()方法的第三个参数用于控制结果中的缩进和空白符。如果这个参数是一个数 值,那它表示的是每个级别缩进的空格数。例如,要在每个级别缩进 4 个空格,可以这样写代码: var book = { "title": "Professional JavaScript", "authors": [ "Nicholas C. Zakas" ], edition: 3, year: 2011 }; var jsonText = JSON.stringify(book, null, 4);
解析选项 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var book = { "title": "Professional JavaScript", "authors": [ "Nicholas C. Zakas" ], edition: 3, year: 2011, 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());
ajax XHR的用法
在使用 XHR 对象时,要调用的第一个方法是 open(),它接受 3 个参数:要发送的请求的类型 (”get”、”post”等)、请求的 URL 和表示是否异步发送请求的布尔值。下面就是调用这个方法的例子。 xhr.open(“get”, “example.php”, false); 这行代码会启动一个针对 example.php 的 GET 请求。有关这行代码,需要说明两点:一是 URL 相对于执行代码的当前页面(当然也可以使用绝对路径);二是调用 open()方法并不会真正发送请求, 而只是启动一个请求以备发送。
1 2 3 要发送特定的请求,必须像下面这样调用 send()方法: xhr.open("get", "example.txt", false); xhr.send(null);
在收到响应后,响应的数据会自动填充 XHR 对象的属性,相关的属性简介如下。
responseText:作为响应主体被返回的文本。 responseXML:如果响应的内容类型是”text/xml”或”application/xml”,这个属性中将保 存包含着响应数据的 XML DOM 文档。 status:响应的 HTTP 状态。 statusText:HTTP 状态的说明
1 2 3 4 5 6 7 xhr.open("get", "example.txt", false); xhr.send(null); if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); }
但多数情况下,我们还是要发送异步请求,才能让JavaScript 继续执行而不必等待响应。此时,可以检测 XHR 对象的 readyState 属性,该属性表示请求/响应过程的当前活动阶段。这个属性可取的值如下。
0:未初始化。尚未调用 open()方法。 1:启动。已经调用 open()方法,但尚未调用 send()方法。 2:发送。已经调用 send()方法,但尚未接收到响应。 3:接收。已经接收到部分响应数据。 4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。
1 2 3 4 5 6 7 8 9 10 11 12 var xhr = createXHR(); 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);
HTTP头部信息
默认情况下,在发送 XHR 请求的同时,还会发送下列头部信息。 Accept:浏览器能够处理的内容类型。 Accept-Charset:浏览器能够显示的字符集。 Accept-Encoding:浏览器能够处理的压缩编码。 Accept-Language:浏览器当前设置的语言。 Connection:浏览器与服务器之间连接的类型。 Cookie:当前页面设置的任何 Cookie。 Host:发出请求的页面所在的域 。 Referer:发出请求的页面的 URI。注意,HTTP 规范将这个头部字段拼写错了,而为保证与规 范一致,也只能将错就错了。(这个英文单词的正确拼法应该是 referrer。) User-Agent:浏览器的用户代理字符串
虽然不同浏览器实际发送的头部信息会有所不同,但以上列出的基本上是所有浏览器都会发送的。 使用 setRequestHeader()方法可以设置自定义的请求头部信息。这个方法接受两个参数:头部字段 的名称和头部字段的值。要成功发送请求头部信息,必须在调用 open()方法之后且调用 send()方法 之前 调用 setRequestHeader()
GET请求 1 2 3 4 5 6 7 8 9 10 11 使用 GET 请求经常会发生的一个错误,就是查询字符串的格式有问题。查询字符串中每个参数的名 称和值都必须使用 encodeURIComponent()进行编码,然后才能放到 URL 的末尾;而且所有名-值对 儿都必须由和号(&)分隔,如下面的例子所示。 xhr.open("get", "example.php?name1=value1&name2=value2", true); 下面这个函数可以辅助向现有 URL 的末尾添加查询字符串参数: function addURLParam(url, name, value) { url += (url.indexOf("?") == -1 ? "?" : "&"); url += encodeURIComponent(name) + "=" + encodeURIComponent(value); return url; }
post请求 1 2 3 4 xhr.open("post", "example.php", true); 发送 POST 请求的第二步就是向 send()方法中传入某些数据。由于 XHR 最初的设计主要是为了处 理 XML,因此可以在此传入 XML DOM 文档,传入的文档经序列化之后将作为请求主体被提交到服务 器。当然,也可以在此传入任何想发送到服务器的字符串。
XMLHttpRequest 2 级
现代 Web 应用中频繁使用的一项功能就是表单数据的序列化,XMLHttpRequest 2 级为此定义了 FormData 类型。FormData 为序列化表单以及创建与表单格式相同的数据(用于通过 XHR 传输)提供 了便利。下面的代码创建了一个 FormData 对象,并向其中添加了一些数据。 var data = new FormData(); data.append(“name”, “Nicholas”); 这个 append()方法接收两个参数:键和值,分别对应表单字段的名字和字段中包含的值。可以像 这样添加任意多个键值对儿。而通过向 FormData 构造函数中传入表单元素,也可以用表单元素的数据 预先向其中填入键值对儿: var data = new FormData(document.forms[0]);
超时设定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var xhr = createXHR(); 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); 在写作本书时,IE 8+仍然是唯一支持超时设定的浏览器。
overrideMimeType()方法 1 2 3 4 5 6 7 比如,服务器返回的 MIME 类型是 text/plain,但数据中实际包含的是 XML。根据 MIME 类型, 即使数据是 XML,responseXML 属性中仍然是 null。通过调用 overrideMimeType()方法,可以保 证把响应当作 XML 而非纯文本来处理。 var xhr = createXHR(); xhr.open("get", "text.php", true); xhr.overrideMimeType("text/xml"); xhr.send(null);
progress事件
Mozilla 对 XHR 的另一个革新是添加了 progress 事件,这个事件会在浏览器接收新数据期间周期 性地触发。而 onprogress 事件处理程序会接收到一个 event 对象,其 target 属性是 XHR 对象,但 包含着三个额外的属性:lengthComputable、position 和 totalSize。其中,lengthComputable 是一个表示进度信息是否可用的布尔值,position 表示已经接收的字节数,totalSize 表示根据 Content-Length 响应头部确定的预期字节数。有了这些信息,我们就可以为用户创建一个进度指示器 了。下面展示了为用户创建进度指示器的一个示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var xhr = createXHR(); xhr.onload = function(event){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ alert(xhr.responseText); } else { alert("Request was unsuccessful: " + xhr.status); } }; 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);
跨域
如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源 信息(如果是公共资源,可以回发”*”)。例如: Access-Control-Allow-Origin: http://www.nczonline.net 如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器 会处理请求。注意,请求和响应都不包含 cookie 信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Firefox 3.5+、Safari 4+、Chrome、iOS 版 Safari 和 Android 平台中的 WebKit 都通过 XMLHttpRequest 对象实现了对 CORS 的原生支持。在尝试打开不同来源的资源时,无需额外编写代码就可以触发这个行 为。要请求位于另一个域中的资源,使用标准的 XHR 对象并在 open()方法中传入绝对 URL 即可,例如: var xhr = createXHR(); 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", "http://www.somewhere-else.com/page/", true); xhr.send(null);
跨浏览器的CORS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function createCORSRequest(method, url){ var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr){ xhr.open(method, url, true); } else if (typeof XDomainRequest != "undefined"){ vxhr = new XDomainRequest(); xhr.open(method, url); } else { xhr = null; } return xhr; } var request = createCORSRequest("get", "http://www.somewhere-else.com/page/"); if (request){ request.onload = function(){ //对 request.responseText 进行处理 }; request.send();
Firefox、Safari 和 Chrome 中的 XMLHttpRequest 对象与 IE 中的 XDomainRequest 对象类似,都 提供了够用的接口,因此以上模式还是相当有用的。这两个对象共同的属性/方法如下。 abort():用于停止正在进行的请求。 onerror:用于替代 onreadystatechange 检测错误。 onload:用于替代 onreadystatechange 检测成功。 responseText:用于取得响应内容。 send():用于发送请求。 以上成员都包含在 createCORSRequest()函数返回的对象中,在所有浏览器中都能正常使用。
JSONP
JSONP 是 JSON with padding(填充式 JSON 或参数式 JSON)的简写,是应用 JSON 的一种新方法, 在后来的 Web 服务中非常流行。JSONP 看起来与 JSON 差不多,只不过是被包含在函数调用中的 JSON, 就像下面这样。 callback({ “name”: “Nicholas” }); JSONP 由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调 函数的名字一般是在请求中指定的。而数据就是传入回调函数中的JSON数据。下面是一个典型的JSONP 请求。http://freegeoip.net/json/?callback=handleResponse
Web Sockets
要说最令人津津乐道的新浏览器 API,就得数 Web Sockets 了。Web Sockets 的目标是在一个单独的 持久连接上提供全双工、双向通信。在 JavaScript 中创建了 Web Socket 之后,会有一个 HTTP 请求发送 到浏览器以发起连接。在取得服务器响应后,建立的连接会使用 HTTP 升级从 HTTP 协议交换为 Web Socket 协议。也就是说,使用标准的 HTTP 服务器无法实现 Web Sockets,只有支持这种协议的专门服 务器才能正常工作。 由于 Web Sockets 使用了自定义的协议,所以 URL 模式也略有不同。未加密的连接不再是 http://, 而是 ws://;加密的连接也不是 https://,而是 wss://。在使用 Web Socket URL 时,必须带着这个 模式,因为将来还有可能支持其他模式。
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 1. Web Sockets API 要创建 Web Socket,先实例一个 WebSocket 对象并传入要连接的 URL: var socket = new WebSocket("ws://www.example.com/server.php"); 注意,必须给 WebSocket 构造函数传入绝对 URL。同源策略对 Web Sockets 不适用,因此可以通 过它打开到任何站点的连接。至于是否会与某个域中的页面通信,则完全取决于服务器。(通过握手信 息就可以知道请求来自何方。) 实例化了 WebSocket 对象后,浏览器就会马上尝试创建连接。与 XHR 类似,WebSocket 也有一 个表示当前状态的 readyState 属性。不过,这个属性的值与 XHR 并不相同,而是如下所示。 WebSocket.OPENING (0):正在建立连接。 WebSocket.OPEN (1):已经建立连接。 WebSocket.CLOSING (2):正在关闭连接。 WebSocket.CLOSE (3):已经关闭连接。 WebSocket 没有 readystatechange 事件;不过,它有其他事件,对应着不同的状态。readyState 的值永远从 0 开始。 要关闭 Web Socket 连接,可以在任何时候调用 close()方法。 socket.close(); 调用了 close()之后,readyState 的值立即变为 2(正在关闭),而在关闭连接后就会变成 3。 2. 发送和接收数据 因为 Web Sockets 只能通过连接发送纯文本数据,所以对于复杂的数据结构,在通过连接发送之前, 必须进行序列化。下面的例子展示了先将数据序列化为一个 JSON 字符串,然后再发送到服务器: var message = { time: new Date(), text: "Hello world!", clientId: "asdfp8734rew" }; socket.send(JSON.stringify(message)); 接下来,服务器要读取其中的数据,就要解析接收到的 JSON 字符串。 当服务器向客户端发来消息时,WebSocket 对象就会触发 message 事件。这个 message 事件与 其他传递消息的协议类似,也是把返回的数据保存在 event.data 属性中。 socket.onmessage = function(event){ var data = event.data; //处理数据 }; 与通过 send()发送到服务器的数据一样,event.data 中返回的数据也是字符串。如果你想得到 其他格式的数据,必须手工解析这些数据。 3.其他事件 WebSocket 对象还有其他三个事件,在连接生命周期的不同阶段触发。 open:在成功建立连接时触发。 error:在发生错误时触发,连接不能持续。 close:在连接关闭时触发
总结
Ajax 是无需刷新页面就能够从服务器取得数据的一种方法。关于 Ajax,可以从以下几方面来总结 一下。 负责 Ajax 运作的核心对象是 XMLHttpRequest(XHR)对象。 XHR 对象由微软最早在 IE5 中引入,用于通过 JavaScript 从服务器取得 XML 数据。 在此之后,Firefox、Safari、Chrome 和 Opera 都实现了相同的特性,使 XHR 成为了 Web 的一个 事实标准。 虽然实现之间存在差异,但 XHR 对象的基本用法在不同浏览器间还是相对规范的,因此可以放 心地用在 Web 开发当中。 同源策略是对 XHR 的一个主要约束,它为通信设置了“相同的域、相同的端口、相同的协议”这一 限制。试图访问上述限制之外的资源,都会引发安全错误,除非采用被认可的跨域解决方案。这个解决 方案叫做 CORS(Cross-Origin Resource Sharing,跨源资源共享),IE8 通过 XDomainRequest 对象支持 CORS,其他浏览器通过 XHR 对象原生支持 CORS。图像 Ping 和 JSONP 是另外两种跨域通信的技术, 但不如 CORS 稳妥。 Comet 是对 Ajax 的进一步扩展,让服务器几乎能够实时地向客户端推送数据。实现 Comet 的手段 主要有两个:长轮询和 HTTP 流。所有浏览器都支持长轮询,而只有部分浏览器原生支持 HTTP 流。SSE (Server-Sent Events,服务器发送事件)是一种实现 Comet 交互的浏览器 API,既支持长轮询,也支持 HTTP 流。 Web Sockets 是一种与服务器进行全双工、双向通信的信道。与其他方案不同,Web Sockets 不使用 HTTP 协议,而使用一种自定义的协议。这种协议专门为快速传输小数据设计。虽然要求使用不同的 Web 服务器,但却具有速度上的优势。 各方面对 Ajax 和 Comet 的鼓吹吸引了越来越多的开发人员学习 JavaScript,人们对 Web 开发的关注 也再度升温。与 Ajax 有关的概念都还相对比较新,这些概念会随着时间推移继续发展。