zoukankan      html  css  js  c++  java
  • javascript 高级技巧详解

    函数的使用技巧

    javascript内置的类型检测机制并非完全可靠。在Object.prototype.toString()方法没有被修改的前提下,可以使用下面的安全检测方法检测是不是原生的:
    function isArray(value){
    return 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]";
    }
    这个技巧可以对任何对象给出正确结论
     
    作用域安全的构造函数
    一般的构造函数都要使用new操作符来调用,但是有时可能会忘记这个函数是不是构造函数,这样会产生错误。为了避免这种错误,要像下面这样进行设计构造函数:
    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);
    }
    }
    这样做的好处是调用它无论是否使用new操作符,都会返回一个Person的新实例,避免了在全局对象上意外设置属性。
    除非你单纯基于构造函数窃取来实现继承,推荐作用域安全的构造函数作为最佳实践。
     
    惰性载入函数
    下面看一个更改的示例:
    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=["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){
      //skip;
    }   
       }
        }
    return new ActiveXObject(arguments.callee.activeXString);
                        };
       }else{
                         createXHR=function(){
         throw new Error("No XHR object available.");
                           };    
       }
                return createXHR();  
    这样的话只会在函数的第一次执行时损失性能,以后执行就不会了。考虑到这个函数是基于浏览器的,这样修改会变得棒极了!
    另一种方式就是把每一部分都变成一个函数,作为返回值返回。这两种之间的区别非常的微妙。
     
    函数的绑定
    函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码的执行环境。由于代码之中存在着this变量,而this在当前环境下指向确定的对象,但是当更改代码的执行环境时,就会出现问题了。为了解决这个问题,javascript函数库中实现了一个bind()函数来解决这个问题。
    一个简单的bind()函数接收一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,并且将所有参数原封不动传递过去。语法如下:
    function bind(fn,context){
       return function(){
       return fn.apply(context,arguments);   
       }
    }
    注意这里使用的arguments并不是bind()的,是内部函数的。
    var handler={
        message:"Event handled",
     
    handleClick:function(event){
      alert(this.message);
    }
    };
    var btn=document.getElementById("my-btn");
    EventUtil.addHandler(btn,"click",bind(handler.handleClick,handler));
    ECMAScript5为所有函数定义了一个原生的bind()方法,进一步简化了操作。
    var handler={
        message:"Event handled",
     
    handleClick:function(event){
      alert(this.message);
    }
    };
    var btn=document.getElementById("my-btn");
    EventUtil.addHandler(btn,"click",handler.handleClick.bind(handler));
    它们主要用于事件处理程序以及setTimeout()和setInterval()。然而被绑定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一些,所以最好只在必要时使用。
     
    函数柯里化
    它用于创建已经设置好了一个或多个参数的函数。函数柯里化的基本方法是:使用一个闭包返回一个函数。当函数被调用时,返回的函数还需要设置一些传入的参数。
    柯里化函数通常由以下步骤动态的创建:调用另一个函数并为它传入要柯里化的函数和必要参数。下面是创建柯里化函数的通用方式:
    function curry(fn){
        var args=Array.prototype.slice.call(arguments,1);
    return function(){
       var innerArgs=Array.prototype.slice.call(arguments);
       var finalArgs=args.concat(innerArgs);
       return fn.apply(null,finalArgs);
    }
    }
    这种变化也需要额外的开销
     
    防篡改对象
    javascript共享的本质一直是一件很头疼的事。但是ECMAScript5解决了这个问题。增加了几个方法,可以指定对象的行为。但是一旦把对象定义为防篡改就无法撤销了。
    不可扩展可以使用这个方法:Object.preventExtensions(obj);
    一旦设置防扩展,对象就无法添加新的属性和方法。已有的属性方法不受影响,这些属性方法仍然可以修改和删除。
    密封对象可以使用方法:Object.seal(obj);
    一旦设置密封对象,不可以进行扩展,已有属性和方法不可以删除,但是属性值是可以修改的。
    冻结对象可以使用方法:Object.freeze(obj);
    一旦设置冻结对象,既不可以扩展,又是密封的,只有通过访问器的set函数才可以修改属性值,其他情况下不允许修改。
    Object.isExtensible(obj);//检测是否可扩展
    Object.isSealed(obj);//检测是否封闭的
    ObjectisFrozen(obj);//检测是否是冻结的
     
    高级定时器
    为了解决setInterval的一些执行问题,下面是采用链式setTimeout的方式来规避:
    setTimeout(function(){
    // add code here
    setTimeout(arguments.callee,interval);
    },interval);
     
    Yielding Processes
    脚本长时间运行的原因:过长的、过深嵌套的函数调用;进行大量的处理循环。
    在展开循环之前,你需要考虑两个重要的问题:
    1:该出事是否必须同步完成?
    2:数据是否必须按顺序完成?
    当你发现有些循环占用大量的事件,同时对上述两个问题答案都是否,那么可以使用定时器来分隔这个循环。
    setTimeout(function(){
    //取出下一个条目处理
    var item=array.shift();
    process(item);
    //还有条目,再设置一个定时器
    if(array.length>0){
    setTimeout(arguments.callee,interval);
    }
    },interval);
     
    函数节流
    对一些持续不断触发的事件,如果建立的事件处理程序不够好的话,会导致浏览器崩溃或者其他的事故。为了规避这个问题,可以使用定时器对事件处理程序进行节流。
    函数节流背后的基本思想是:一些代码不可以在没有间断的情况下连续重复执行。具体做法是:第一次调用函数,创建一个定时器,在指定的时间间隔后运行代码。当第二次调用该函数时,它会清除前一次的定时器,并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意义。然而如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的就是执行函数的请求停止了一段时间之后才执行。以下是该模式的基本形式:
    var processor={
         timeoutId:null,
     
     performProcessing:function(){
       //实际执行的处理程序
    },
     process:function(){
       clearTimeout(this.timeoutId);
       var that=this;
       this.timeoutId=setTimeout(function(){
    that.performProcessing();   
      },1000);
     }
    };
    processor.process();//尝试开始执行
    只要代码是周期性执行的,都应该用节流。处理的速率根据需求设置,上面的例子是1000毫秒。
     
    自定义事件
    事件的模式是采用观察者模式建立的。下面是一段自定义事件的代码示例,注意观察和体会:
    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);
       },
       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);
       }
       },
       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++){
      if(handlers[i]===handler)break;  
      }
       }
       }    
    }
    当其他的节点继承EventTarget的时候,它也会有上面的事件行为了。
     
    拖放
    这是一个非常流行的自定义事件。非常不客气的说,如果你不会,你就不要说自己学过自定义事件。
    var DragDrop=function(){
        var dragging=null;
    function handleEvent(event){
        var event=EventUtil.getEvent(event);
    var target=EventUtil.getTarget(event);
     
    switch(event.type){
        case "mousedown":     if(target.className.indexOf("draggable")>-1){
                          dragging=target;  
       }
       break;
    case  "mousemove":     if(dragging!=null){
                          dragging.style.left=event.clientX+"px";
      dragging.style.top=event.clientY+"px";
       }
       break;
        case   "mouseup":      dragging=null;
                           break; 
    }
    };
    return {
      enable:function(){
         EventUtil.addHandler(document,"mousedown",handleEvent);
     EventUtil.addHandler(document,"mousemove",handleEvent);
     EventUtil.addHandler(document,"mouseup",handleEvent);
      },
       disable:function(){
         EventUtil.removeHandler(document,"mousedown",handleEvent);
     EventUtil.removeHandler(document,"mousemove",handleEvent);
     EventUtil.removeHandler(document,"mouseup",handleEvent);
       }   
    }
    }
    注意元素被拖放,他必须是绝对定位。
    <div class="draggable" style="position:absolute;background:red"/>
    但是使用之后会发现一些瑕疵,我们可以做一些进一步的修改
    var DragDrop=function(){
        var dragging=null,diffX=0,diffY=0;
    function handleEvent(event){
        var 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;
       }
       break;
    case  "mousemove":     if(dragging!=null){
                          dragging.style.left=(event.clientX-diffX)+"px";
      dragging.style.top=(event.clientY-diffY)+"px";
       }
       break;
        case   "mouseup":      dragging=null;
                           break; 
    }
    };
    return {
      enable:function(){
         EventUtil.addHandler(document,"mousedown",handleEvent);
     EventUtil.addHandler(document,"mousemove",handleEvent);
     EventUtil.addHandler(document,"mouseup",handleEvent);
      },
       disable:function(){
         EventUtil.removeHandler(document,"mousedown",handleEvent);
     EventUtil.removeHandler(document,"mousemove",handleEvent);
     EventUtil.removeHandler(document,"mouseup",handleEvent);
       }   
    }
    }
    这时需要提供一些触发的自定义事件了
    var DragDrop=function(){
        var dragdrop=new EventTarget(),dragging=null,diffX=0,diffY=0;
    function handleEvent(event){
        var 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;    
     
    }
    还可以为switch里面的事件处理程序添加更多变化的事件处理程序,这样自定义拖放事件就做好了
  • 相关阅读:
    一元试用一个月的备份服务,把编剧的套路彻底堵死
    微软开源 PowerShell 并支持 Linux 和 OS X
    MySQL Database on Azure 支持 5.7 版本啦!
    python把字典写入excel之一例
    python字典无序?有序?
    字典转化为有序列表
    对xml文件的sax解析(增删改查)之二
    对xml文件的sax解析(增删改查)之一
    XML中CDATA和#PCDATA的区别
    dtd文件中写的引用实体被xml文件引用后无法在浏览器中显示的问题
  • 原文地址:https://www.cnblogs.com/hansu/p/3978393.html
Copyright © 2011-2022 走看看