高程笔记8
数据存储 Cookie
HTTP Cookie,通常直接叫做 cookie,最初是在客户端用于存储会话信息的。该标准要求服务器对 任意 HTTP 请求发送 Set-Cookie HTTP 头作为响应的一部分,其中包含会话信息。例如,这种服务器响 应的头可能如下: HTTP/1.1 200 OK Content-type: text/html Set-Cookie: name=value Other-header: other-header-value
由于 cookie 是存在客户端计算机上的,还加入了一些限制确保 cookie 不会被恶意使用,同时不会占 据太多磁盘空间。每个域的 cookie 总数是有限的,不过浏览器之间各有不同。如下所示。
IE6 以及更低版本限制每个域名最多 20 个 cookie。 IE7 和之后版本每个域名最多 50 个。IE7 最初是支持每个域名最大 20 个 cookie,之后被微软的 一个补丁所更新。 Firefox 限制每个域最多 50 个 cookie。 Opera 限制每个域最多 30 个 cookie。 Safari 和 Chrome 对于每个域的 cookie 数量限制没有硬性规定。
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 48 49 50 51 52 53 54 55 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);
Web存储机制
Web Storage 的两个主要目标是: 提供一种在 cookie 之外存储会话数据的途径; 提供一种存储大量可以跨会话存在的数据的机制。 最初的 Web Storage 规范包含了两种对象的定义:sessionStorage 和 globalStorage。
Storage 类型
Storage 类型提供最大的存储空间(因浏览器而异)来存储名值对儿。Storage 的实例与其他对 象类似,有如下方法。 clear(): 删除所有值;Firefox 中没有实现 。 getItem(name):根据指定的名字 name 获取对应的值。 key(index):获得 index 位置处的值的名字。 removeItem(name):删除由 name 指定的名值对儿。 setItem(name, value):为指定的 name 设置一个对应的值。
sessionStorage 对象
sessionStorage 对象存储特定于某个会话的数据,也就是该数据只保持到浏览器关闭。这个对象 就像会话 cookie,也会在浏览器关闭后消失。存储在 sessionStorage 中的数据可以跨越页面刷新而 存在,同时如果浏览器支持,浏览器崩溃并重启之后依然可用(Firefox 和 WebKit 都支持,IE 则不行)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 //使用方法存储数据 sessionStorage.setItem("name", "Nicholas"); //使用属性存储数据 sessionStorage.book = "Professional JavaScript"; //使用方法读取数据 var name = sessionStorage.getItem("name"); //使用属性读取数据 var book = sessionStorage.book; 使用 for-in 循环来迭代 sessionStorage 中的值: for (var key in sessionStorage){ var value = sessionStorage.getItem(key); alert(key + "=" + value); } //使用 delete 删除一个值——在 WebKit 中无效 delete sessionStorage.name; //使用方法删除一个值 sessionStorage.removeItem("book");
3. globalStorage 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 //保存数据 globalStorage["wrox.com"].name = "Nicholas"; //获取数据 var name = globalStorage["wrox.com"].name; /* 在这里,访问的是针对域名 wrox.com 的存储空间。globalStorage 对象不是 Storage 的实例, 而具体的 globalStorage["wrox.com"]才是。这个存储空间对于 wrox.com 及其所有子域都是可以 访问的。可以像下面这样指定子域名。 */ //保存数据 globalStorage["www.wrox.com"].name = "Nicholas"; //获取数据 var name = globalStorage["www.wrox.com"].name;
4.localStorage 对象 localStorage 对象在修订过的 HTML 5 规范中作为持久保存客户端数据的方案取代了 globalStorage。与 globalStorage 不同,不能给 localStorage 指定任何访问规则;规则事先就 设定好了。要访问同一个 localStorage 对象,页面必须来自同一个域名(子域名无效),使用同一种 协议,在同一个端口上。这相当于 globalStorage[location.host]。
5.Storage限制
对于 localStorage 而言,大多数桌面浏览器会设置每个来源 5MB 的限制。Chrome 和 Safari 对每 个来源的限制是 2.5MB。而 iOS 版 Safari 和 Android 版 WebKit 的限制也是 2.5MB。 对 sessionStorage 的限制也是因浏览器而异。有的浏览器对 sessionStorage 的大小没有限制, 但 Chrome、Safari、iOS 版 Safari 和 Android 版 WebKit 都有限制,也都是 2.5MB。IE8+和 Opera 对 sessionStorage 的限制是 5MB。
IndexedDB
数据库 IndexedDB 就是一个数据库,与 MySQL 或 Web SQL Database 等这些你以前可能用过的数据库类似。 IndexedDB 最大的特色是使用对象保存数据,而不是使用表来保存数据。一个 IndexedDB 数据库,就是 一组位于相同命名空间下的对象的集合。1 2 3 4 5 6 7 8 9 var request, database; request = indexedDB.open("admin"); request.onerror = function(event){ alert("Something bad happened while trying to open: " + event.target.errorCode); }; request.onsuccess = function(event){ database = event.target.result; };
最 佳 实 践 / 代 码 风 格 命名的一般规则如下所示。
变量名应为名词如 car 或 person。 函数名应该以动词开始,如 getName()。返回布尔类型值的函数一般以 is 开头,如 isEnable()。 变量和函数都应使用合乎逻辑的名字,不要担心长度。长度问题可以通过后处理和压缩(本章 后面会讲到)来缓解。
变量类型透明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //通过初始化指定变量类型 var found = false; //布尔型 var count = -1; //数字 var name = ""; //字符串 var person = null; //对象 第二种方法是使用匈牙利标记法来指定变量类型。 //用于指定数据类型的匈牙利标记法 var bFound; //布尔型 var iCount; //整数 var sName; //字符串 var oPerson; //对象 //用于指定类型的类型注释 var found /*:Boolean*/ = false; var count /*:int*/ = 10; var name /*:String*/ = "Nicholas"; var person /*:Object*/ = null;
解耦应用逻辑/事件处理程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function validateValue(value){ value = 5 * parseInt(value); if (value > 10){ document.getElementById("error-msg").style.display = "block"; } } function handleKeyPress(event){ event = EventUtil.getEvent(event); if (event.keyCode == 13){ var target = EventUtil.getTarget(event); validateValue(target.value); } } 以下是要牢记的应用和业务逻辑之间松散耦合的几条原则: 勿将 event 对象传给其他方法;只传来自 event 对象中所需的数据; 任何可以在应用层面的动作都应该可以在不执行任何事件处理程序的情况下进行; 任何事件处理程序都应该处理事件,然后将处理转交给应用逻辑。 牢记这几条可以在任何代码中都获得极大的可维护性的改进,并且为进一步的测试和开发制造了很 多可能。
避免全局量 与尊重对象所有权密切相关的是尽可能避免全局变量和函数。这也关系到创建一个脚本执行的一致 的和可维护的环境。最多创建一个全局变量,让其他对象和函数存在其中。请看以下例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //两个全局量——避免!! var name = "Nicholas"; function sayName(){ alert(name); } 这段代码包含了两个全局量:变量 name 和函数 sayName()。其实可以创建一个包含两者的对象, 如下例所示: //一个全局量——推荐 var MyApplication = { name: "Nicholas", sayName: function(){ alert(this.name); } };
避免与 null 进行比较 1 2 3 4 5 6 7 8 9 10 11 12 13 14 由于 JavaScript 不做任何自动的类型检查,所有它就成了开发人员的责任。因此,在 JavaScript 代码 中其实很少进行类型检测。最常见的类型检测就是查看某个值是否为 null。但是,直接将值与 null 比较是使用过度的,并且常常由于不充分的类型检查导致错误。看以下例子: function sortArray(values){ if (values != null){ //避免! values.sort(comparator); } } 比如数组,可以改造成 function sortArray(values){ if (values instanceof Array){ //推荐 values.sort(comparator); } }
如果看到了与 null 比较的代码,尝试使用以下技术替换: 如果值应为一个引用类型,使用 instanceof 操作符检查其构造函数; 如果值应为一个基本类型,使用 typeof 检查其类型; 如果是希望对象包含某个特定的方法名,则使用 typeof 操作符确保指定名字的方法存在于对 象上。 代码中的 null 比较越少,就越容易确定代码的目的,并消除不必要的错误。
使用常量 尽管 JavaScript 没有常量的正式概念,但它还是很有用的。这种将数据从应用逻辑分离出来的思想, 可以在不冒引入错误的风险的同时,就改变数据。请看以下例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function validate(value){ if (!value){ alert("Invalid value!"); location.href = "/errors/invalid.php"; } } 改成: var Constants = { INVALID_VALUE_MSG: "Invalid value!", INVALID_VALUE_URL: "/errors/invalid.php" }; function validate(value){ if (!value){ alert(Constants.INVALID_VALUE_MSG); location.href = Constants.INVALID_VALUE_URL; } }
关键在于将数据和使用它的逻辑进行分离。要注意的值的类型如下所示。 重复值——任何在多处用到的值都应抽取为一个常量。这就限制了当一个值变了而另一个没变 的时候会造成的错误。这也包含了 CSS 类名。 用户界面字符串 —— 任何用于显示给用户的字符串,都应被抽取出来以方便国际化。 URLs ——在 Web 应用中,资源位置很容易变更,所以推荐用一个公共地方存放所有的 URL。 任意可能会更改的值 —— 每当你在用到字面量值的时候,你都要问一下自己这个值在未来是不 是会变化。如果答案是“是”,那么这个值就应该被提取出来作为一个常量。
作用域 1.避免全局查找 可能优化脚本性能最重要的就是注意全局查找。使用全局变量和函数肯定要比局部的开销更大,因 为要涉及作用域链上的查找。请看以下函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function updateUI(){ var imgs = document.getElementsByTagName("img"); for (var i=0, len=imgs.length; i < len; i++){ imgs[i].title = document.title + " image " + i; } var msg = document.getElementById("msg"); msg.innerHTML = "Update complete."; } 该函数可能看上去完全正常,但是它包含了三个对于全局 document 对象的引用。如果在页面上有 多个图片,那么 for 循环中的 document 引用就会被执行多次甚至上百次,每次都会要进行作用域链 查找。通过创建一个指向 document 对象的局部变量,就可以通过限制一次全局查找来改进这个函数的 性能: function updateUI(){ var doc = document; var imgs = doc.getElementsByTagName("img"); for (var i=0, len=imgs.length; i < len; i++){ imgs[i].title = doc.title + " image " + i; } var msg = doc.getElementById("msg"); msg.innerHTML = "Update complete."; }
1. 多个变量声明
有个地方很多开发人员都容易创建很多语句,那就是多个变量的声明。很容易看到代码中由多个 var 语句来声明多个变量,如下所示: //4 个语句——很浪费 var count = 5; var color = “blue”; var values = [1,2,3]; var now = new Date(); 在强类型语言中,不同的数据类型的变量必须在不同的语句中声明。然而,在 JavaScript 中所有的 变量都可以使用单个 var 语句来声明。前面的代码可以如下重写: //一个语句 var count = 5, color = “blue”, values = [1,2,3], now = new Date();