第二十二章 高级技巧
一、高级函数
1、安全的类型检测
1 function isArray(value){
2 return Object.prototype.toString.call(value) == "[object Array]";
3 }
也可以基于这一思路测试某个值是不是原生函数或正则表达式:(这一技巧广泛用来检测原生JSON对象)
1 //判断是否原生函数
2 function isFunction(value){
3 return Object.prototype.toString.call(value) == "[object Function]";
4 }
5 //判断是否原生函数
6 function isFunction(value){
7 return Object.prototype.toString.call(value) == "[object RegExp]";
8 }
1 function Person(name,age,job){
2 if(this instanceof Person){ //判断this是否是正确的类型
3 this.name = name;
4 this.age = age;
5 this.job = job;
6 }else{
7 return new Person(name,age,job);
8 }
9 }
10
11 var per1 = Person("Nicholas",29,"Software Engineer");
12 alert(window.name); //""
13 alert(per1.name); //"Nicholas"
14
15 var per2 = new Person("Shelby",34,"Ergonomist");
16 alert(per2.name); //"Shelby"
多个程序员在一个页面上写JavaScript代码的环境中,推荐使用作用域安全的构造函数作为最佳实践。
3、惰性载入函数
对于多分支的if语句,有些时候并不需要每次都查询if语句,对于此情况,引入了惰性载入技巧,惰性载入表示函数分支只会执行一次,有两种方法可以实行该技巧:
方法1、在函数被调用时处理函数,在第一次调用时该函数会被覆盖为另一个按合适方式执行的函数。
1 function createXHR(){
2 if(typeof XMLHttpRequest != "undefined"){
3 createXHR = function(){ //将原函数覆盖
4 return new XMLHttpRequest();
5 };
6 }else if(typeof ActiveXObject != "undefined"){
7 createXHR = function(){ //将原函数覆盖
8 if(typeof arguments.callee.activeXString != "string"){
9 var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len;
10 for(i=0,len=versions.length;i < len;i++){
11 try{
12 new ActiveXObject(versions[i]);
13 arguments.callee.activeXString = versions[i];
14 break;
15 }catch(ex){
16 //跳过
17 }
18 }
19 }
20 return new ActiveXObject(arguments.callee.activeXString);
21 };
22 }else{
23 createXHR = function(){ //将原函数覆盖
24 throw new Error("No XHR object available.");
25 };
26 }
27 return createXHR();
28 }
方法2、在声明函数时指定适当的函数,这样第一次调用函数不损失性能,在代码首次加载时会损失性能
1 var createXHR = (function(){
2 if(typeof XMLHttpRequest != "undefined"){
3 return function(){
4 return new XMLHttpRequest();
5 };
6 }else if(typeof ActiveXObject != "undefined"){
7 return function(){
8 if(typeof arguments.callee.activeXString != "string"){
9 var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],i,len;
10 for(i=0,len=versions.length;i < len;i++){
11 try{
12 new ActiveXObject(versions[i]);
13 arguments.callee.activeXString = versions[i];
14 break;
15 }catch(ex){
16 //跳过
17 }
18 }
19 }
20 return new ActiveXObject(arguments.callee.activeXString);
21 };
22 }else{
23 return function(){
24 throw new Error("No XHR object available.");
25 };
26 }
27 })();
4、函数绑定
函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用。
1 var handler = {
2 message : "Event handled",
3
4 handleClick : function(event){
5 alert(this.message);
6 }
7 };
8
9 var btn = document.getElementById("my-btn");
10 EventUtil.addHandler(btn,"click",handler.handleClick); //按钮按下显示undefined,不显示Event handled
上述代码的问题在于没有保存handler.handleClick的环境,所以this最后指向了DOM按钮,而非handler(IE8中this指向window),可以使用闭包解决问题:
1 var handler = {
2 message : "Event handled",
3
4 handleClick : function(event){
5 alert(this.message);
6 }
7 };
8
9 var btn = document.getElementById("my-btn");
10 EventUtil.addHandler(btn,"click",function(event){
11 handler.handleClick(event); //按钮按下显示Event handled
12 });
由于闭包难以理解和调试,大部分js库为此实现了可以将函数绑定到指定环境的函数,一般叫做bind(),简单的bind函数,接收一个函数和环境,返回一个给定函数中调用给定函数的函数,并且将所有参数原封不动传过去。
1 //bind函数解决方案
2 function bind(fn,context){
3 return function(){
4 return fn.apply(context,arguments);
5 };
6 }
5、函数柯里化
函数柯里化用于创建已经设置好了一个或多个参数的函数。基本方法和函数绑定一样:使用闭包返回一个函数。区别在于:函数被调用时返回的函数还需要设置一些传入参数。
二、防篡改对象
1、不可扩展对象
1 var person = { name : "name"};
2 Object.preventExtensions(person);
3
4 person.age = 28;
5 alert(person.age); //undefined
6 //Object.isExtensible()可检测对象是否可扩展
1 var person = { name : "name" };
2 alert(Object.isExtensible(person)); //true
3 alert(Object.isSealed(person)); //false
4
5 Object.seal(person);
6 alert(Object.isExtensible(person)); //false
7 alert(Object.isSealed(person)); //true

JavaScript中经常以事件的形式应用观察者模式。虽然事件常常和DOM一起使用,但也可以通过实现自定义事件在自己的代码中应用。使用自定义事件有助于将不同部分的代码相互之间解耦,让维护更加容易,并减少引入错误的机会。
拖放对于桌面和Web应用都是一个非常流行的用户界面范例,它能够让用户非常方便地以一种直观的方式重新排列或者配置东西。在JavaScrip中可以使用鼠标事件和一些简单的计算来实现这种功能类型。将拖放行为和自定义事件结合起来可以创建一个可重复使用的框架,它能应用于各种不同的情况下。
第二十三章 离线应用与客户端存储
开发离线Web应用需要几个步骤。首先是确保应用知道设备是否能上网,以便下一步执行正确的操作。然后,应用还必须能访问一定的资源(图像、JavaScript、CSS 等),只有这样才能正常工作。 最后,必须有一块本地空间用于保存数据,无论能否上网都不妨碍读写。HTML5及其相关的API让开发离线应用成为现实。
一、离线检测
HTML5定义了navigator.onLine属性,true表示能上网。
1 var socket = new WebSocket("ws://www.example.com/server.php");
2 var CookieUtil = {
3 get:function(name){
4 var cookieName = encodeURIComponent(name) + "=" ,
5 cookieStart = document.cookie.indexOf(cookieName),
6 cookieValue = null;
7
8 if(cookieStart > -1){
9 var cookieEnd = document.cookie.indexOf(";",cookieStart);
10 if(cookieEnd == -1){
11 cookieEnd = document.cookie.length;
12 }
13 cookieValue = decodeURIComponent(document.cookie.substring(cookieStart+cookieName.length,cookieEnd));
14 }
15
16 return cookieValue;
17 },
18 set:function(name,value,expires,path,domain,secure){
19 var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
20
21 if(expires instanceof Date){
22 cookieText += "; expires=" + expires.toGMTString();
23 }
24 if(path){
25 cookieText += "; path=" + path;
26 }
27 if(domain){
28 cookieText =+ "; domain=" + domain;
29 }
30 if(secure){
31 cookieText += "; secure=" + secure;
32 }
33 document.cookie = cookieText;
34 },
35 unset:function(name,path,domain,secure){
36 this.set(name,"",new Date(0),path,domain,secure);
37 }
38 };
使用上述方法的例子:
1 //设置cookie
2 CookieUtil.set("name","Nicholas");
3 CookieUtil.set("book","Professional JavaScript");
4
5 //读取
6 alert(CookieUtil.get("name")); //"Nicholas"
7 alert(CookieUtil.get("book")); //Professional JavaScript
8
9 //删除
10 CookieUtil.unset("name");
11 CookieUtil.unset("book");
12
13 //设置cookie,包括它的路径、域、失效日期
14 CookieUtil.set("name","Nicholas","/books/projs","www.wrox.com",new Date("January 1,2010"));
15
16 //删除刚设置的cookie
17 CookieUtil.unset("name","/books/projs","www.wrox.com");
18
19 //设置安全cookie
20 CookieUtil.set("name","Nicholas",null,null,null,true);
子 cookie 是存放在单个 cookie 中的更小段的数据。也就是使用cookie值来存储多个名称值对 儿。子cookie常见的的格式如下所示:
name=name1=value1&name2=value2&name3=value3&name4=value4&name5=value5
注:使用cookie时尽量少存储信息,不存敏感信息,因为cookie数据并非存储在一个安全环境中。