zoukankan      html  css  js  c++  java
  • 高级程序设计(第3版)第二十二章高级技巧/笔记

    高级技巧

    • 高级函数
      • 安全的类型检测
    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]";
    } 
      • 作用域安全的构造函数
        • 作用域安全的构造函数在进行任何更改前,首先确认 this 对象是正确类型的实例。
        • 如果不是,那 么会创建新的实例并返回。
    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的,this会被解析成window 对象
    var person1 = Person("Nicholas", 29, "Software Engineer");
    alert(window.name);  //"" 
    alert(person1.name);     //"Nicholas" 
     
    var person2 = new Person("Shelby", 34, "Ergonomist");
    alert(person2.name);     //"Shelby" 
        • 继承的问题
          • 构造函数式     继承失败
          • 原型+构造函数式     继承成功
    function Polygon(sides){ 
      if (this instanceof Polygon) { 
        this.sides = sides;
        this.getArea = function(){ 
          return 0; 
        };  
      } else { 
        return new Polygon(sides);  
      }
    } 
    function Rectangle(width, height){ 
      Polygon.call(this, 2);    //返回一个新的Polygon对象,里面的属性方法不会成为Rectangle的属性和方法
      this.width = width; 
      this.height = height;
        this.getArea = function(){
        return this.width * this.height; 
      };
    } 
    ----------------------------------------------------
    
    var rect = new Rectangle(5, 10);
    alert(rect.sides); //undefined 
    
    ----------------------------------------------------
    
    Rectangle.prototype = new Polygon(); //使得,一个Rectangle实例也同时是一个Polygon实例
    var rect = new Rectangle(5, 10); 
    alert(rect.sides);        //2

     

      • 惰性载入函数
        • 惰性载入表示函数执行的分支仅会发生一次,避免了大量的if语句,避免了不必要的代码
    //法一:
    //在第一次调用的过程中,该函数会被覆盖为另外一个按合适方式执行的函数,
    //这样任何对原函数的调用都不用再经过执行的分支了
    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(); 
    } 
    //第二种实现惰性载入的方式是在声明函数时就指定适当的函数。
    //这样,第一次调用函数时就不会损 失性能了,而在代码首次加载时会损失一点性能
    var createXHR = (function(){   
      if (typeof XMLHttpRequest != "undefined"){
        return function(){      
          return new XMLHttpRequest();  
        };   
      } else if (typeof ActiveXObject != "undefined"){   
          return 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 { 
        return function(){     
          throw new Error("No XHR object available.");   
        };    
      }
    })(); 

     

      • 函数绑定
        • 函数绑定要创建一个函数,可以在特定的 this 环境中 以指定参数调用另一个函数。
        • 该技巧常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境
        • 被绑 定函数与普通函数相比有更多的开销,它们需要更多内存,同时也因为多重函数调用稍微慢一点,所 以好只在必要时使用。
        • 一个简单的 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 + ":" + event.type); 
        } 
    }; 
    //自定义的bind
    var btn = document.getElementById("my-btn"); 
    EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler)); 
    
    //原生的 bind()方法
    //不用 再自己定义 bind()函数了,而是可以直接在函数上调用这个方法
    var btn = document.getElementById("my-btn"); 
    EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler));

     

      • 函数柯里化
        • 函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。
        • 两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。
        • 柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数

     

    • 防篡改对象
      • 不可扩展对象
        • 默认情况下,所有对象都是可以扩展的。也就是说,任何时候都可以向对象中添加属性和方法
        • Object.preventExtensions()方法可以改变这个行为,让你不能再给对象添加属性和方法。
        • 虽然不能给对象添加新成员,但已有的成员则丝毫不受影响。你仍然还可以修改和删除已有的成员
        • 另外,使用 Object.istExtensible()方法还可以确定对象是否可以扩展。  

     

    var person = {
      name: "Nicholas"
    }; 
    Object.preventExtensions(person); 
    person.age = 29; 
    alert(person.age); //undefined 
    
    //------------------------------------------------
    
    var person = { 
      name: "Nicholas"
    };
    alert(Object.isExtensible(person));  //true 
    Object.preventExtensions(person); 
    alert(Object.isExtensible(person));  //false
      • 密封的对象
        • 密封对象不可扩展,而 且已有成员的[[Configurable]]特性将被设置为 false。这就意味着不能删除属性和方法,因为不能 使用 Object.defineProperty()把数据属性修改为访问器属性,或者相反。
        • 属性值是可以修改的。
        • 使用 Object.isSealed()方法可以确定对象是否被密封了
        • 因为被密封的对象不可扩展,所以用 Object.isExtensible()检测密封的对象也会返回 false。
    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 
      • 冻结的对象
        • 冻结的对象既不可扩展,又是密封的,而且对象 数据属性的[[Writable]]特性会被设置为 false
        • 如果定义[[Set]]函数,访问器属性仍然是可写的。
        • Object.freeze()方法可以用来冻结对象。
        • Object.isFrozen()方法用于检测冻结对象
        • 因为冻结对象既是密封的又是不可 扩展的,所以用 Object.isExtensible()和 Object.isSealed()检测冻结对象将分别返回 false 和 true。  

     

    • 高级定时器
      1. 浏览器负责进行排序,指派某段代码在某个时间点运行 的优先级
      2. 在 JavaScript中没有任何代码是立刻执行的,但一旦进程空闲则尽快执行
      3. 定时器对队列的工作方式是,当特定时间过去后将代码插入。
      4. 设定一个 150ms后执行的定时器不代表到了 150ms代码就立刻 执行,它表示代码会在 150ms后被加入到队列中
      • 重复的定时器
        • 定时器代码可能在代码再次被添加到队列之前还没有完成执行,结果导致定时器代码连续运行好几次, 而之间没有任何停顿。
        • 当使用 setInterval()时,仅 当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中。
        • 这种重复定时器的规则有两个问题:
          • 某些间隔会被跳过
          • 多个定时器的代码执行之间的间隔 可能会比预期的小。
        • 解决:使用链式setTimeout() 调用
    setTimeout(function(){ 
     
        //处理中 
     
        setTimeout(arguments.callee, interval); 
     
    }, interval); 
    
    //这个模式链式调用了 setTimeout(),每次函数执行的时候都会创建一个新的定时器。
    //第二个 setTimeout()调用使用了 arguments.callee 来获取对当前执行的函数的引用,并为其设置另外一个定时器。
    //这样做的好处是,在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保 不会有任何缺失的间隔。
    //而且,它可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行。
      • Yielding Processes 二刷预定
        • 脚本长时间运行的问题通常有两个原因
          • 过长的、过深嵌套的函数调用或者是进行大 量处理的循环
        • 当你发现某个循环占用了大量时间,同时对于“该处理是否必须同步完成?”和“数据是否必须按顺序完成?”两个问题,你的回答都是“否”时,你就可以 使用定时器分割这个循环。这是一种叫做数组分块(array chunking)的技术,小块小块地处理数组,通 常每次一小块。基本的思路是为要处理的项目创建一个队列,然后使用定时器取出下一个要处理的项目 进行处理,接着再设置另一个定时器。
    //基本模式
    setTimeout(function(){  
      //取出下一个条目并处理
      var item = array.shift();
      process(item); 
     
      
      //若还有条目,再设置另一个定时器 
      if(array.length > 0){  
        setTimeout(arguments.callee, 100); 
      } 
    }, 100)
     
    
    function chunk(array, process, context){
      setTimeout(function(){  
        var item = array.shift();   
        process.call(context, item); 
     
            if (array.length > 0){  
              setTimeout(arguments.callee, 100);    
            }   
      }, 100);
    } 
      • 函数节流
        • 函数节流背后的基本思想是指,某些代码不可以在没有间断的情况连续重复执行。
    //基本模式
    var processor = {    
      timeoutId: null, 
     
        //实际进行处理的方法 
      performProcessing: function(){
        //实际执行的代码  
      }, 
     
        //初始处理调用的方法 
      process: function(){ 
        clearTimeout(this.timeoutId); 
        var that = this;    
        this.timeoutId = setTimeout(function(){    
          that.performProcessing();   
        }, 100);   
      } 
    }; 
     
    //尝试开始执行  processor.process(); 
        • throttle()函数
          • 可以自动进行定时器的设置和清除
          • 接受两个参数:要执行的函数以及在哪个作用域中执行
        • 节流在 resize 事件中是常用的
        • 只要代码是周期性执行的,都应该使用节流,但是你不能控制请求执行的速率。
    //简化版(使用 throttle()函数)
    function throttle(method, context) {  
      clearTimeout(method.tId);    
      method.tId= setTimeout(function(){    
        method.call(context);    
      }, 100); 
    } 
    • 自定义事件
    function EventTarget(){   
      this.handlers = {}; //用于储存事件处理程序
    } 
     
    EventTarget.prototype = {
      constructor: EventTarget,   
      addHandler: function(type, handler){  //法接受两个参数:事件类型和用于处理该事件的函数
        if (typeof this.handlers[type] == "undefined"){ //handlers 属性中是否已经存在一个针对该事件类型的数组
          this.handlers[type] = [];   //;如果没有,则创建一个新的数组
        } 
     
       this.handlers[type].push(handler); //使用 push()将该处理程序添加到数组的末尾。 
      }, 
     
        fire: function(event){    //接受一个单独的参数,是一个至少包含 type 属性的对象。
          if (!event.target){   //给 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);
          }  
        }
    }; 
    • 拖放

    拖放的基本概念很简单:创建一个绝对定位的元素,使其可以用鼠标移动

      • 修缮拖动功能
    var DragDrop = function(){  
     
      var 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;    
            }           
            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); 
            } 
       } 
    }();     
    View Code
      • 添加自定义事件二刷预定

    //这一章看得我眼珠子都要掉了,难顶,缓一缓再二刷叭~

  • 相关阅读:
    启用div作为编辑器 添加contentEditalbe属性
    AngularJs 通过 ocLazyLoad 实现动态(懒)加载模块和依赖-转
    angularjs的懒加载
    JavaScript 中的this指向问题
    Project Euler:Problem 41 Pandigital prime
    Android 消息机制
    新西兰天维网登录发送明文password
    使用Reveal来查看别人的APP界面+白苹果不刷机解决方式
    Android中List循环遍历性能对照
    2016年最新苹果开发人员账号注冊申请流程最强具体解释!
  • 原文地址:https://www.cnblogs.com/isremya/p/13443280.html
Copyright © 2011-2022 走看看