《JavaScript 高级程序设计》笔记(7)
Canvas、HTML5 脚本编程、JSON、Ajax 与 Comet、高级技巧
第 15 章 使用 Canvas 绘图
canvas 元素、 2D 上下文、 WebGL。
-
getContext() 获得绘图上下文对象的引用
-
toDataURL(‘图像MIME’) 导出绘制的对象,同一域
2D 上下文
-
fillStyle / strokeStyle 字符串、渐变对象或模式对象
-
fillRect()
-
strokeRect()
-
clearRect()
-
beginPath()
-
arc(x, y, radius, startAngle, endAngle, counterclockwise)
-
arcTo()
-
bezierCurveTo()
-
lineTo(x, y)
-
moveTo(x, y)
-
quadraticCurveTo()
-
rect()
…
WebGL
参看《HTML5 与 WebGL 编程》
…
第 16 章 HTML5 脚本编程
跨文档消息传送 (XDM, cross-document messaging)
- postMessage(“消息”, “发送消息的文档所在域/*”) 向 iframe 元素或者由当前窗口弹出的窗口传递数据。
var iframeWindow = document.getElementById('myframe').contentWindow;
iframeWindow.postMessage("消息", "http://llissery.net")
- 收到 XDM 消息会触发 window 对象的 message 事件,包含信息 :
- data 字符串数据
- origin 发送消息的文档所在的域
- source 发送消息的文档的 window 对象的代理,只能调用 postMessage()
原生拖放
能够在框架间、窗口间、应用间拖放网页元素
拖放事件
- 拖动某元素会依次触发 : dragstart 、 drag 、 dragend
- 当某个元素被拖动到一个有效的放置目标上时,依次发生 :dragenter 、 dragover 、 dragleave 或 drop
自定义放置目标
- 不允许放置的元素不会发生 drop 事件
- 重写 dragenter 、 dragover 事件的默认行为可以把任何元素变成游戏的放置目标
dataTransfer 对象 事件对象的属性,用于在拖放操作时实现数据交换
-
getData(‘text’/‘URL’、‘value’)
-
setData(‘text’/‘URL’) HTML5 也支持 “text” 和 “URL”,但这两种类型会被映射为 “text/plain” 和 “text/uri-list”
-
dropEffect : 被拖动的元素能执行哪种放置行为 (“none”/“move”/“copy”/“link”) ,仅改变光标样式,在 ondragenter 事件处理程序中针对放置目标设置。
-
effectAllowed : 运行拖动元素的哪种 dropEffect (dropEffect 属性只有搭配 effectAllowed 属性才有用),在 ondragstart 事件处理程序中设置 effectAllowed 属性。
- copy : 复制到新的位置
- move : 移动到新的位置
- link : 建立一个源位置到新位置的链接
- copyLink : 允许复制或者链接
- copyMove : 允许复制或者移动
- linkMove : 允许链接或者移动
- all : 允许所有的操作
- none : 禁止所有操作
- uninitialized : 缺省值(默认值), 相当于 all
- 分配任何其他值时不会有任何影响并且保留旧值
-
addElement(element)
-
clearData(format)
-
setDragImage(element, x, y)
-
types
可拖动
- 默认情况,只有图像、链接、被选中的文本是可拖动的
- HTML5 为所有 HTML 元素规定了一个 draggable 属性 (true/false),IE10+
媒体元素 (IE9+)
audio 元素、video 元素
-
src:指向要加载的媒体文件
-
poster:指定图像的 URI 可以在加载视频内容期间显示一幅图像
-
controls:浏览器应该显示 UI 控件
-
并非所有浏览器都支持所有媒体格式,所以可以指定多个不同的媒体来源。为此,不用在标签中指定 src 属性,而是要像下面这样使用一或多个 source 元素。
<!-- 嵌入视频 -->
<video id="myVideo">
<source src="Video.webm" type="video/webm; codecs='vp8, vorbis'">
<source src="Video.ogv" type="video/ogg; codecs='theora, vorbis'">
<source src="Video.mpg">
Video player not available.
</video>
<!-- 嵌入音频 -->
<audio id="myAudio">
<source src="song.ogg" type="audio/ogg">
<source src="song.mp3" type="audio/mpeg">
Audio player not available.
</audio>
属性 p487 事件 p488
自定义媒体播放器 : play() 和 pause() 方法
检测编解码器的支持情况 : canPlayType()
Audio 类型 : 类似 Image,但不必插入到文档
历史状态管理
hashchange 事件
-
状态管理 API
-
history.pushState(状态对象({name:“name”}), “新状态标题”, [相对 URL]) : 将新信息加入历史状态栈,改变浏览器地址栏(不会发送请求)
-
“后退”按钮会触发 popstate 事件 (state 属性包含当初以第一个参数传递给 pushState() 的状态对象),在这里手动重置页面状态
-
history.replaceState(状态对象, 新状态标题) : 不会再历史状态栈中创建新状态,只会重写当前状态
第 17 章 错误处理与调试
错误处理
try-catch 语句
try {
// code
} catch (error) {
// 处理错误
} [ finally {
// finally
}
]
- 错误对象 : message name
- 错误类型 : Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError
抛出错误
- throw : 抛出自定义错误,值无类型要求
- throw new ..Error(“message”).. ..Error.prototype = new Error();
error 事件
错误会触发 window 对象的 error 事件,并执行 window.onerror 事件处理程序(接收 错误消息、错误URL、行号 三个参数)
第 18 章 JavaScript 与 XML
第 19 章 E4X
第 20 章 JSON
语法
简单值 对象 数组
- JSON 的字符串必须引用双引号
- 不支持 undefined
- 对象和数组通常是 JSON 数据结构的最外层形式
解析与序列化
JSON 对象
-
JSON 对象序列化 :JSON.stringify(object, 过滤器(数组/函数/null), 缩进(数值,所有>10的都会自动转换成10)或者空白格或者字符串)
- 如果过滤器是数组,结果只包含数组中列出的属性
- 如果过滤器是函数,接收 属性名和属性值 返回相应的键值,返回 underfined 的时候就忽略该属性
- 可以为任何对象添加 toJSON() 方法 toJSON : function(){return this.name;}
-
JSON 字符串转换为 JavaScript 值 :JSON.parse(json, 还原函数)
- 还原函数返回 undefined 则删除,返回其它值则插入该值
- 可以给任何对象添加 releaseDate() 方法 releaseDate:new Date(2011,11,1)
第 21 章 Ajax 与 Comet
XMLHttpRequest 对象
IE7+ : var xhr = new XMLHttpRequest();
// IE7 之前的版本 用 var xhr = createXHR() 来创建
function createXHR () {
if (typeof XMLHttpRequest != "undefined") {
return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined") {
if (typeof arguments.callee.activeXString != "string") {
var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
i, len;
for (i=0, len=versions.length; i<len; i++) {
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex) {}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("No XHR object available.");
}
}
XHR 用法
- open(‘get’/‘post’, url, 是否异步) : 启动一个请求以备发送
- send(数据/null) : 发送特定请求
- abort() : 在接收到响应之前取消异步请求
收到响应后,响应的数据会自动填充 XHR 对象的属性
- responseText : 响应主体被返回的文本
- responseXML : 响应的内容类型如果是 ‘text/html’ / ‘application/xml’,这个属性将保存包含着响应数据的 XML DOM 文档
- status : 响应的 http 状态
- statusText : http 状态的说明
- readyState : 请求/响应过程的当前活动阶段
- 0 : 未初始化,未 open
- 1 : 启动,已 open 未 send
- 2 : 发送,已 send 未 收到响应
- 3 : 接收,已 收到部分响应
- 4 : 完成,已 收到全部响应
- 属性变化会触发 readystatechange 事件,在调用 open() 之前指定事件处理程序才能确保浏览器的兼容性
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', 'url', true);
xhr.send(null);
HTTP 头部信息
请求附带信息
-
Accept : 浏览器能够处理的内容类型
-
Accept-Charset : 浏览器能够显示的字符集
-
Accept-Encoding : 浏览器能够处理从颜色编码
-
Accept-Language : 浏览器当前设置语言
-
Connection : 浏览器与服务器之间的连接类型
-
Cookie : 当前页面设置的任何 Cookie
-
Host : 发出请求的页面所在的域
-
Referer : 发出请求的页面的 URI
-
User-Agent : 浏览器的用户代理字符串
-
setRequestHeader(name, value) : 设置自定义的请求头部信息, open() 之后,send() 之前。
-
getResponseHeader(name) : 取得单个头部信息
-
getAllResponsetHeaders() : 取得所有的头部信息
Get 请求
- 查询字符串的格式 : 使用 encodeURIComponent() 进行编码,使用 & 分割。
POST 请求
- 要记得先设置 Content-Type,
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));
XMLHttpRequest 2 级
- FormData : 为序列化表单和创建与表单格式相同的数据提供便利【Chorme、Safri5+、Firefox4+】
xhr.open("post","postexample.php", true);
var form = document.getElementById("user-info");
xhr.send(new FormData(form));
- 超时设定 : timeout 只适用于IE8
xhr. timeout =1000;
xhr.ontimeout = function(){
alert("Request took too long.");
};
- overrideMimeType() : 重写MIME类型, send() 之前调用
xhr.overrideMimeType("text/xml");IE 不支持
进度事件
open 之前添加事件处理程序
- loadstart : 在接收到响应数据的第一个字节时触发
- error : 在请求发送错误时触发
- abort : 在因为调用 abort() 方法二终止时触发
- load 事件 : (IE8 仅支持此事件),浏览器接收到服务器的完整的响应,不管结果如何,都会触发
- loadend : 在通信完成或触发 error abort load 后触发
- progress 事件 : 接收数据期间周期性触发
- lengthComputable : 进度信息是否可用
- position : 已接收字节数
- totalSize : Content-Length 头部缺点的预期字节数
跨源资源共享
CORS (Cross-Origin Resource Sharing, 跨域资源共享) 定义了再必须访问跨源资源时,浏览器与服务器应该如何沟通。(请求和响应不包含 cookie 信息)
- 发送请求时,附加头部 Origin: http://xxx.net (协议,域名,端口)
- 服务认可后,响应发回同源信息 Access-Control-Allow-Origin: http://xxx.net (或 * )
- 浏览器匹配 Access-Control-Allow-Origin 头部信息,不匹配会驳回
CSRF (Cross-Site Request Forgery) : 跨站点请求伪造 XSS (Cross-Site Scripting) : 跨站点脚本
IE 对 CORS 的实现
IE 8 XDR 类型
其它浏览器对 CORS 的实现
XMLHttpRequest 使用绝对 URL
- 不能使用 setRequestHeader() 设置自定义的请求头部信息
- 不能发送和接收 cookie
- getAllResponsetHeaders() 返回空字符串
Preflighted Requests IE11+
CORS 通过一种叫做 Preflighted Requests 的透明服务器验证机制支持开发人员使用自定义的头部、 GET 或 POST 之外的方法,以及不同类型的主体内容。
- 浏览器使用 OPTIONS 方法发送一个 Preflight 请求,包含头部 :
- Origin : 与简单请求相同
- Access-Control-Request-Method : 请求自身使用的方法
- Access-Control-Request-Headers : (可选)自定义的头部信息,多个头部以逗号分隔
- 服务器响应 :
- Access-Control-Allow-Origin : 与简单的请求相同
- Access-Control-Allow-Methods : 允许的方法,以逗号分隔
- Access-Control-Allow-Headers : 允许的头部,以逗号分隔
- Access-Control-Max-Age : 这个 Preflight 请求的缓存时间(s)
带凭据的请求
-
将 withCredentials 属性设置为 true
-
Access-Control-Allow-Credentials : true 指定某个请求应该发送凭据
其他跨域技术
图像 Ping : 创建一个 Image 实例,然后将 onload onerror 事件处理函数指定为同一个函数。通常用于跟踪用户点击页面或动态广告的曝光次数。
JSONP : JSON with padding 包含在函数中的 JSON,由 回调函数 和 数据 两部分组成。安全性和确定请求是否加载成功问题。
Comet : 一种服务器向页面推送数据的技术
- 长轮询 : 浏览器发起一个请求到服务器,服务器一直保持打开状态,直到有数据可发送。(短轮训浏览器定时向服务器发送请求查询是否有更新)。
- HTTP 流 : 在页面的整个生命周期内只使用一个 HTTP 连接。
- createStreamingClient(要连接的 URL、在接收到数据时调用的函数以及关闭连接时调用的函数)】
服务器发送事件 SSE(Server-Sent Events)
- SSE API
var source = EventSource("URL");
source.readyState // 0=正在连接到服务器 1=打开了连接 2=关闭了连接
// open 事件 : 建立连接时触发
// message 事件 : 在从服务器接收到新事件时触发
// error 事件 : 在无法建立连接时触发
// 数据保存在 event.data 字符串中
source.onmessage = function (event) {
var data = event.data;
...
}
source.close(); // 立即断开连接
- 事件流 : 服务器事件通过一个持久的 HTTP 响应发送, MIME 类型 为 text/event-stream 。
data: 有空行时会触发 message 事件 id: 关联事件 id 位于 data: 前后皆可
Web Socket
WebSocket 的目标是在一个单独的持久连接上提供全双工、双向通信。
JavaScript 中创建了 WebSocket 之后,会有一个 HTTP 请求发送到浏览器以发起连接。在取得服务器响应后,建立的链接会使用 HTTP 升级 从 HTTP 协议交换为 WebSocket 协议(ws:// wss://)。
发送数据量小
API :
-
var socket = new WebSocket(url);
-
socket.readyState
- WebSocket.OPENING (0) : 正在建立连接
- WebSocket.OPEN (1) : 已经建立连接
- WebSocket.CLOSEING (2) : 正在关闭连接
- WebSocket.CLOSE (3) : 已经关闭连接
-
发送连接 : socket.send(‘message’)
-
关闭连接 : socket.close() readyState -> 2 -> 3
-
message 事件 : 收到消息
-
open 事件 : 在成功建立连接时触发
-
error 事件 : 在发生错误时触发,连接不能持续
-
close 事件 : 在连接关闭时触发
SSE 与 Web Socket
- 是否需要双向通信
- 是否有自由度建立和维护 WebSocket 服务器
第 22 章 高级技巧
高级函数
安全的类型检测
使用 Object.prototype.toString.call(value) 返回值与 "[object xxx]" 进行比较判断类型。
作用域安全的构造函数 : 解决不使用 new 调用构造函数产生的问题
-
构造函数中,在进行任何更改前,首先确认 this 是正确类型的实例(this instanceof X)。如果不是,创建新的实例(new X)并返回。
-
会锁定可以调用构造函数的环境,如果使用构造函数窃取模式的继承而且不使用原形链,这个继承就有可能被破坏掉
-
当一个构造函数继承一个使用作用域安全的构造函数的时候,使用原型链或寄生组合的方式解决。里面要使用 Person.call(this, ‘ccl’, 23); 来继承,还需要在这个函数下面使用 Student.prototype = new Person();
惰性载入函数 : 函数执行的分支仅会发生一次
-
函数被调用时再处理函数 (重写函数)
-
在声明函数时创建一个匿名、自执行的函数,指定适当的函数 ( var xx = (function(){..if.. return function(){})(); )
函数绑定
创建一个函数,在特定的 this 环境中以指定参数调用另一个函数,通常与回调函数与事件处理程序一起使用。
var handler = {
message: "Event handled",
handleClick: function (event) {
alert(this.message);
}
};
// 1. 使用闭包
xx.onclick = function (event) { handler.handleClick(event); };
// 2. 自己创建 bind() 创建一个bind()函数接受一个函数和一个环境,并返回给定环境中调用给定函数,并且将所有参数原封不动传递过去
function bind (fn, context) {
return function () {
return fn.apply(context, argument);
}
}
xx.onclick = bind(handler.handleClick, handler);
// 3. 原生 bind()
xx.onclick = handler.handleClick.bind(handler);
函数柯里化 : 使用一个闭包返回一个函数
- 创建柯里化的通用方式
function curry (fn) {
var args = Array.prototype.slice.call(arguments, 1); // 1 表示返回数组包含从第二个参数开始 的所有参数
return function () {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);
};
}
// 调用
var curriedAdd = curry(add, 5); // 这里的add后面可以添加多个参数
- 作为函数绑定的一部分包含在其中,创造出更复杂的 bind() 函数
function bind(fn, context) {
var args = Array.prototype.slice.call(arguments, 2);
return function () {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(context, finalArgs);
};
}
// 使用
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));
- 利用原生的 bind() 方法 :
EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler, "my-btn"));
防篡改对象 (属性定义为防篡改后无法撤销)
不可扩展对象
不能向对象添加属性和方法
- Object.preventExtensions(person)
- Object.isExtensible(person)
密封的对象
密封对象不可扩展,而且 [[configuraable]] 会设置为 false,也就是不能删除属性和方法,但是属性值是可以修改的, 在非严格模式下,删除和修改属性会被忽略,但是在严格模式下,会抛出错误
- Object.seal(person)
- Object.isSealed(person)
冻结对象
既不可以扩展,又是密封的,而且对象的 [[writable]] 被设置为 false, 在非严格模式下,对冻结的对象执行非法操作会被忽略,但是在严格模式下,会抛出错误
- Object.Froze(person)
- Object.isFrozen(person)
高级定时器
重复定时器
- setInterval() : 定时器代码规则地插入队列中。问题 : (1)某些间隔会被跳过; (2)多个定时器的代码执行之间的间隔可能比预期的小。
- 链式调用 setTimeout() : 解决上述问题
setTimeout(function(){ //处理中 setTimeout(arguments.callee, interval);}, interval);Yielding Processes
浏览器中的 JavaScript 资源受限制。
- 数组分块
function chunk(array, process, context) {
setTimeout(function () {
var item = array.shift();
process.call(context, item);
if (array.length > 0) {
setTimeout(arguments.callee, 100);
}
}, 100);
}
函数节流
某些代码不可以在没有间断的情况连续执行。 resize 事件中最常用。
function throttle (method, context) {
clearTimeout(method.tId);
method.tId = setTimeout(function () {
method.call(context);
}, 100);
}
function resizeDiv () {...}
window.onresize = function () {
throttle(resizeDiv);
}
自定义事件
事件是一种 观察者 设计模式,观察者模式由 主体和观察者 两类对象组成。
// 创建一个管理事件对象,让其他对象监听那些事件
function EventTarget () {
this.handlers = {};
}
EventTarget.prototype = {
constructor : EventTarget,
// 注册给定类型的事件处理程序
addHandler : function (type, handler) {
if (typeof this.handlers[type] == "undefined") {
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
// 触发一个事件
fire : function (event) {
if (!event.target) {
event.target = this;
}
if (this.handlers[event.type] instanceof Array) {
var handlers = this.handlers[event.type];
for (var i=0, len=handlers.length; i < len; i++) {
handlers[i](event);
}
}
},
// 注销某个事件类型的事件处理程序
removeHandler : function (type, handler) {
if (this.handlers[type] instanceof Array) {
var handlers = this.handlers[type];
for (var i=0, len=handlers.length; i < len; i++) {
if (handlers[i] === handler) {
break;
}
}
handlers.splice(i, 1);
}
}
};
使用
function handleMessage (event) {
alert("Message received: " + event.message);
}
//创建一个新对象
var target = new EventTarget();
//添加一个事件处理程序
target.addHandler("message", handleMessage);
//触发事件
target.fire({ type: "message", message: "Hello world!"});
//删除事件处理程序
target.removeHandler("message", handleMessage);
//再次,应没有处理程序
target.fire({ type: "message", message: "Hello world!"});
继承 EventTarget 并获得这个行为
function Person(name, age) {
EventTarget.call(this);
this.name = name;
this.age = age;
}
inheritPrototype(Person, EventTarget);
Person.prototype.say = function (message) {
this.fire({type: "message", message: message});
};
拖放:
var DragDrop = function () {
var dragdrop = new EventTarget(),
dragging = null,
diffX = 0,
diffY = 0;
function handleEvent (event) {
//获取事件和对象
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
//确定事件类型
switch (event.type) {
case "mousedown":
if (target.className.indexOf("draggable") > -1) {
dragging = target;
diffX = event.clientX - target.offsetLeft;
diffY = event.clientY - target.offsetTop;
dragdrop.fire({type:"dragstart", target: dragging, x: event.clientX, y: event.clientY});
}
break;
case "mousemove":
if (dragging !== null) {
//指定位置
dragging.style.left = (event.clientX - diffX) + "px";
dragging.style.top = (event.clientY - diffY) + "px";
//触发自定义事件
dragdrop.fire({type:"drag", target: dragging,
x: event.clientX, y: event.clientY});
}
break;
case "mouseup":
dragdrop.fire({type:"dragend", target: dragging, x: event.clientX, y: event.clientY});
dragging = null;
break;
}
}
//公共接口
dragdrop.enable = function () {
EventUtil.addHandler(document, "mousedown", handleEvent);
EventUtil.addHandler(document, "mousemove", handleEvent);
EventUtil.addHandler(document, "mouseup", handleEvent);
};
dragdrop.disable = function () {
EventUtil.removeHandler(document, "mousedown", handleEvent);
EventUtil.removeHandler(document, "mousemove", handleEvent);
EventUtil.removeHandler(document, "mouseup", handleEvent);
};
return dragdrop;
}();
第 23 章 离线应用与客户端存储
离线检测
确定网络是否可用 :
- navigator.onLine : true 能上网,不同浏览器之间还是有些差异
- online 和 offline 事件 在页面加载后,最好先通过 navigator.onLine 取得初始的状态。然后,就是通过上述两个事件来确定网络连接状态是否变化。
应用缓存 (appcache)
Appcache : 从浏览器的缓存中的一块缓存区。 描述文件 : manifest
- applicationCache() 对象
- status 状态
- 事件 checking / error / noupdate / downloading / progress / updateready / cached
- applicationCache.update() 检测描述文件是否更新
- applicationCache.swapCache()
数据存储
Cookie
服务器对任意 HTTP 请求发送 Set-Cookie HTTP 头作为响应的一部分,其中包含会话信息。(名称和值都必须经过 URL 编码, decodeURIComponent() 解码) 浏览器会存储会话信息,在这之后为每个请求添加 Cookie HTTP 头将信息(Cookie: xxx=xx)发送回服务器。
-
限制 : 绑定在特定域名下;每个域的 cookie 总数是有限制的,超出会删除之前的 cookie;cookie 的尺寸也有限制 4095B,超出后 cookie 会被丢弃。
-
构成 :
- 名称 不区分大小写 必须被 URL 编码
- 值 必须被 URL 编码
- 域 对哪个域是有效的
- 路径 对指定域中的那个路径发送 cookie
- 失效时间
- 安全标志 指定后只有在使用 SSL 连接时才发送
-
document.cookie
// 读取 写入 删除
var CookieUtil = {
get : function (name) {
var cookieName = encodeURIComponent(name) + "=",
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null;
if (cookieStart > -1) {
var cookieEnd = document.cookie.indexOf(";", cookieStart);
if (cookieEnd == -1) {
cookieEnd = document.cookie.length;
}
cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
}
return cookieValue;
},
set : function (name, value, expires, path, domain, secure) {
var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
if (expires instanceof Date) {
cookieText += "; expires=" + expires.toGMTString();
}
if (path) {
cookieText += "; path=" + path;
}
if (domain) {
cookieText += "; domain=" + domain;
}
if (secure) {
cookieText += "; secure";
}
document.cookie = cookieText;
},
unset : function (name, path, domain, secure) {
this.set(name, "", new Date(0), path, domain, secure);
}
};
使用
//设置 cookie
CookieUtil.set("name", "Nicholas");
CookieUtil.set("book", "Professional JavaScript");
//读取 cookie 的值
alert(CookieUtil.get("name")); //"Nicholas"
alert(CookieUtil.get("book")); //"Professional JavaScript"
//删除 cookie
CookieUtil.unset("name");
CookieUtil.unset("book");
//设置 cookie,包括它的路径、域、失效日期
CookieUtil.set("name", "Nicholas", "/books/projs/", "www.wrox.com",
new Date("January 1, 2010"));
//删除刚刚设置的 cookie
CookieUtil.unset("name", "/books/projs/", "www.wrox.com");
//设置安全的 cookie
CookieUtil.set("name", "Nicholas", null, null, null, true);
子 cookie
var SubCookieUtil = {
get : function (name, subName) {
var subCookies = this.getAll(name);
if (subCookies) {
return subCookies[subName];
} else {
return null;
}
},
getAll : function(name) {
var cookieName = encodeURIComponent(name) + "=",
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null,
cookieEnd,
subCookies,
i,
parts,
result = {};
if (cookieStart > -1) {
cookieEnd = document.cookie.indexOf(";", cookieStart);
if (cookieEnd == -1) {
cookieEnd = document.cookie.length;
}
cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd);
if (cookieValue.length > 0) {
subCookies = cookieValue.split("&");
for (i=0, len=subCookies.length; i < len; i++) {
parts = subCookies[i].split("=");
result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
}
return result;
}
}
return null;
},
//省略了更多代码
};
使用
//假设 document.cookie=data=name=Nicholas&book=Professional%20JavaScript
//取得全部子 cookie
var data = SubCookieUtil.getAll("data");
alert(data.name); //"Nicholas"
alert(data.book); //"Professional JavaScript"
//逐个获取子 cookie
alert(SubCookieUtil.get("data", "name")); //"Nicholas"
alert(SubCookieUtil.get("data", "book")); //"Professional JavaScript"
IE 用户数据
- 使用 CSS 在某个元素上指定 userData 行为,就可以使用 setAttribute() 方法来保存数据
<div style="behavior:url(#default#userData)" id="dataStore"></div>
var dataStore = document.getElementById("dataStore");
dataStore.setAttribute("name", "Nicholas");
dataStore.setAttribute("book", "Professional JavaScript");
dataStore.save("BookInfo");
- 下一次载入时,可以使用 load() 来获取数据
dataStore.load("BookInfo");
dataStore.getAttribute("name")
- removeAttribute()方法明确指定要删除某元素数据,只要指定属性名称。删除之后,必须像下面这样再次调用 save()来提交更改。
dataStore.removeAttribute("name");
dataStore.save("BookInfo");
Web 存储机制
Web Storage 目的 : 提供一种在 cookie 之外存储会话数据的途径;提供一种存储大量可跨会话存在的数据机制
-
sessionStorage globalStorage IE8+
-
storage 类型(字符串)
- clear() : 删除所有值; Firefox 中没有实现
- getItem(name) : 可以直接调用
- key(index) 获得 index 位置处的值的名字
- removeItem(name) 可以直接调用
- setItem(name, value) 可以直接调用
- length 值对数量,无法判断对象中所有数据的大小,不过 IE8 提供了一个 remainingSpace 属性,用于获取还可以使用的存储空间的字节数
-
sessionStorage 对象 : 存储定于某个会话的数据,保持数据到浏览器关闭
- 存储方法
- 使用方法存储数据 : sessionStorage.setItem(“name”, “Nicholas”);
- 使用属性存储数据 : sessionStorage.book = “Professional JavaScript”;
- IE8 强制写入磁盘 : sessionStorage.begin();sessionStorage.name = “Nicholas”;sessionStorage.book = “Professional JavaScript”;sessionStorage.commit();
- 读取数据
- 使用方法存储数据 : var name = sessionStorage.getItem(“name”);
- 使用属性存储数据 : var book = sessionStorage.book;
- 结合 length 属性和 key() 方法来迭代 sessionStorage 中的值: for (var i=0, len = sessionStorage.length; i < len; i++){var key = sessionStorage.key(i);var value = sessionStorage.getItem(key);}
- 使用 for-in 循环来迭代 sessionStorage 中的值 : for (var key in sessionStorage){var value = sessionStorage.getItem(key);alert(key + “=” + value);}
- 删除数据
- 使用 delete 删除一个值(在 WebKit 中无效) : delete sessionStorage.name;
- 使用方法删除一个值 : sessionStorage.removeItem(“book”);
- 存储方法
-
globalStorage 对象 : 跨会话存储数据
- 保存数据 globalStorage[“wrox.com”].name = “Nicholas”;
- 获取数据 var name = globalStorage[“wrox.com”].name; 事先不能确定域名,那么使用 location.host 作为属性名比较安全
-
localStorage 对象 : 域名 协议 端口
//使用方法存储数据
localStorage.setItem("name", "Nicholas");
//使用属性存储数据
localStorage.book = "Professional JavaScript";
//使用方法读取数据
var name = localStorage.getItem("name");
//使用属性读取数据
var book = localStorage.book;
// 兼容只支持 globalStorage 的浏览器
function getLocalStorage () {
if (typeof localStorage == "object") {
return localStorage;
} else if (typeof globalStorage == "object") {
return globalStorage[location.host];
} else {
throw new Error("Local storage not available.");
}
}
// 使用
var storage = getLocalStorage();
-
storage 事件 (IE8 和 Firefox 只实现了 domain 属性)
- domain : 发生变化的存储空间的域名
- key : 设置或者删除的键名
- newValue : 如果是设置值,则是新值;如果是删除键,则是 null
- oldValue : 键被更改之前的值
-
侦听 storage 事件
EventUtil.addHandler(document, "storage", function(event){
alert("Storage changed for " + event.domain);
});
IndexedDB (浏览器中保存结构化数据的一种数据库)
- 方法完全异步,每一次 IndexedDB 操作,都需要注册 onerror 或 onsuccess 事件处理程序,以确保适当地处理结果
var request, database, errorInfo;
if (database.version != "1.0") {
request = database.setVersion("1.0");
request.onerror = function(event) {
errorInfo = event.target.errorCode;//错误信息
};
request.onsuccess = function(event) {
database = event.target.result;//数据库实例对象
};
} else {
alert("Database already initialized. Database name: " + database.name + ", Version: " + database.version);
}
第 24 章 最佳实践
可维护性
性能
部署
第 25 章 新兴的 API
requestAnimationFrame()
早期使用 setInterval() 来控制所有动画
IE10+
Page Visibility API
-
document.hidden : 页面是否隐藏
-
document.visibilityState : 状态
-
visibilitychange 事件
Geolocation API
- navigator.gelocation : getCurrentPosition()