js高级技巧笔记(一)

  • 安全的类型检测

Js的类型检测机制并非完全可靠,发生错误否定及错误肯定的情况也不少:

safari 在对正则表达式应用typeof操作符时返回”function”,因此很难确定某个值到底是不是函数

Instanceof在包含多个框架的时候出现错误.

解决上述问题的办法都一样,在任何值上调用Object原生的toString方法,都会返回一个[object NativeConstructorName]格式的字符串,每个类内部都有一个[[Class]]属性,这个属性中就指定了上述字符串中的构造函数,例如

alert(Object.prototype.toString.call(value));//”[object Array]”

同样,也可以基于这一思路来测试某个值是不是原生函数或正则表达式:

function isFunction(value){

    return Object.prototype.toString.call(value)==”[object Function]”;

}

function isRegExp(value){

    return Object.prototype.toString.call(value)==”[object RegExp]”;

}

不过要注意,对应在ie中以COM对象形式实现的任何函数,isFunction()都将返回false

  • 作用域安全

js在自定义对象的构造函数的定义和用法是这样:

function Person(name,age,job){
      this.name=name;
      this.age=age;
      this.job=job;
}
var person=new Person(“Nicholas”,29,”Software Engineer”);

上面的例子如果不用new,this将会指向window对象,导致全局变量的污染,而且之前在一个大神的博客中提到,其实全局变量的性能消耗要比局部变量的性能消耗相对要大,他得出这个结论的论据是,js中有作用域(也叫执行环境)的概念,函数在查找里面的变量时候先会查找局部变量(即当前函数作用域)当找不到的时候在去找上一级的作用域以此类推直到window全局作用域,尽量声明局部变量可以避免这样逐级找的性能消耗.

解决上面可能污染全局变量的风险的方法如下:

function Person(name,age,job){
       if(this instanceof Person){
           this.name=name;
           this.age=age;
           this.job=job;
       }else{
          return new Person(name,age,job);
       }
}
var person1=Person(“Nicholas”,29,”Software Rnginneer”);
alert(window.name);//””
alert(person1.name);//”Nicholas”
var person2=new Person(“shelby”,34,”Ergonnmist”);
alert(person2.name);

这样就不会有污染全局变量的风险,当然这只是简单的封装,jQuery源码中有更为复杂全面的封装,这里就不做说明,明年肯定要话半年的时间看jQuery源码,可能最多只能学到jQuery里面的皮毛.

  • 惰性载入函数

 因为浏览器之间的行为的差异,多数JavaScript代码都包含大量的if语句,将执行引导到正确的代码中,看看下面代码:

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”,”MSXL2.XMLHttp”],i,len;
      for(i=0,len=versions.length;i<len;i++){
       try{
           new ActiveXObject(versions[i]);
           Arguments.callee.activeXString=versions[i];
          }catch(ex){

          }
         }
      }
      return new ActiveXObject(arguments.caller.activeXString);
   }else{
     throw new Error(“No XHR object available”)
   }
}

每次调用这个方法的时候,它都要对浏览器所支持的能力仔细检查,首先检查内置的XHR,然后测试有没有基于ActiveXXHR,最后如果都没有发现的话就抛出一个错误,每次调用该函数都是这样,所以如果if语句不必每次执行,俺么代码的可以运行的更快一些,可以用惰性载入来解决.

creatrXHR().


function createXHR(){
 if(typeof XMLHttpRequest!=”undefined”){
 createXHR=function(){
  return new XMLHttpRequest();
 }
 }else if(typeof ActiveXObject !=”undefined”){
  createXHR=function(){
     if(typeof arguments.callee.activeXString !=”string”){
      var versions=[“MSXNL2.XMLHttp.6.0”,”MSXNL2.XNLHttp.3.0”,”MSXML2.XNLHttp”],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{
createXHR=function(){
  Throw new Error(“No XHR object available”);
}
  }
return createXHR();

}

在这个懒性载入的creaXHR,if语句的每一个分支都会为creaXHR变量赋值,有效覆盖了原有的函数,最后一步便是调用新赋值的函数

第二种实现懒性载入的方式是在声明函数时就指定适当的函数,这样,第一次调用函数时就不会损失性能了,而在代码首次加载时会损失一点性能.

var createXHR=(function(){
   if(typeof XMLHttpRequest !=”undefined”){
         return funciton(){
             return new XMLHttpRequest();
          }
   }else if(typeof ActiveXObject !=”undefined”){
     return funciton(){
     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{
      return function(){
           throw new Brror(“No XHR object available”);
      }
   }
})()
  • 函数绑定

函数绑定就是用this来保留函数的代码执行环境.例如:

var handler={
  message:”Event handled”,
  handleClick:funcion(event){
      alert(this.message);
  }
};

var btn=document.getElementById(“my-btn”);

EventUtil.addHandler(btn,”click”,function(event){
   handler.handleClick(event);
});

这里用了闭包,因为单击事件的函数this会指向dom对象,加个闭包就保留函数原本的this对象.es5中有个原生的bind方法来绑定this.

  • 函数柯里化

函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数.两者的区别在于,但函数被调用时,返回的函数还需要设置一些传入的参数.

柯里化通用方式如下:

function curry(fn){


  var args=Array.prototype.slice.call(arguments,1);//注意这里是将非第一个的参数转换为数组,因为arguments本身不是数值只是用法很像数组

  return function(){
   var innerArgs=Array.prototype.slice.call(arguments);
   var finalArgs=args.concat(innerArgs);//将参数合并
    return fn.apply(null,finalArgs);
  }  
}

这只是简单的柯里化,下面是柯里化实际应用代码:

function bind(fn,context){
 var args=Array.prototype.slice.call(arguments);
 return function(){
var innerArgs=Array.prototype.slice.call(arguments);
var finalArgs=args.concat(innerArgs);
return fn.apply(context,finalArgs);
 }
}
var handler={
   message:”Event handled”,
   bandleClick:function(name,event){
    alert(this.message+”:”+name+”;”+event.type);
   }
};
var btn=document.getElementById(“my-btn”);
EventUtil.addHandler(btn,”click”,bind(handler.handleClick,handler,”my-btn”));

Es5bind方法也能实现函数的柯里化,只要在this的值之后再传入另一个参数即可.例如:

var handler={
  message:”Event handled”,
  handleClick:function(name,event){
      alert(this.message+”:”+name+”:”+event.type);
  }
};
var btn=doucument.getElementById(“my-btn”);
EventUtil.addHandler(btn,”click”,hander.handlerClick.bind(handler,”my-btn”));

另外柯里化不太适合在项目功能层使用,第一使用不当容易造成内存泄漏,第二功能层是业务功能实现层,这层在后期维护,系统优化改动是最大的,所以业务层必须要保证代码的可读性,代码注释齐全,复杂的函数回调尽量少用,而在基础封装层也就是通用基础功能封装层可以用这种代码,第一这层改动比较少,第二代码复杂,第三要保证用的人简单,这层一般是组长级别的来维护. 

  • 防篡改对象

      1.不可扩展对象

Es5提供对改变属性的行为,使用Object.preventExtensions()方法可以让指定的对象不能添加属性.例如:

var person={name:”Nicholas”};
Object.preventExtensions(person);
person.age=29;
alert(person.age);//undefined

可以用Object.isExtensible()方法来判断对象是否可以扩展.例如:

var person={name:”Nicholas”};
alert(Object.isExtensible(person));//true

Object.preventExtensions(person);
alert(Object.isExtensible(person));//false

      2.密封的对象

Es5可以通过Object.seal方法将对象设置为密封对象,不能添加也不能删除属性,例如:

var person={name:”Nicholas”};
Object.seal(person);

 
person.age=29;
alert(person.age);//undefined


delete person.name;
alert(person.name)//”Nicholas”

Object.isSealed()方法可以确定对象是否被密封,例如:

var person={name:”Nicholas”};
alert(Object.isExtensible(person));//true
alert(Object.isSealed(person));//false

 
Object.seal(person);
alert(Object.isExtensible(person));//false
alert(Object.isSealed(person));//true

      3.冻结的对象

Es5可以用object.freeze冻结对象

Object.isFrozen方法用于检测冻结对象

 

 

 

 

 

posted @ 2017-01-09 14:29 核桃大号 阅读(...) 评论(...) 编辑 收藏