Erik's Blog

《JavaScript 高级程序设计》笔记(7)

(更新于 )JavaScript#JavaScript

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 信息)

  1. 发送请求时,附加头部 Origin: http://xxx.net (协议,域名,端口)
  2. 服务认可后,响应发回同源信息 Access-Control-Allow-Origin: http://xxx.net (或 * )
  3. 浏览器匹配 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 之外的方法,以及不同类型的主体内容。

  1. 浏览器使用 OPTIONS 方法发送一个 Preflight 请求,包含头部 :
  • Origin : 与简单请求相同
  • Access-Control-Request-Method : 请求自身使用的方法
  • Access-Control-Request-Headers : (可选)自定义的头部信息,多个头部以逗号分隔
  1. 服务器响应 :
  • 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()

File API

Web 计时

Web Workers IE10+