zoukankan      html  css  js  c++  java
  • 读书笔记:javascript高级技巧(一)

    一.安全的类型检测

    javascript内置的类型检测机制并非完全可靠,由于浏览器或者作用域等原因,经常会发生错误。大家知道,在任何值调用toString()方法都会返回一个[object Native ConstructorName]格式的字符串,每个类内部都有一个[class]属性,这个属性就指定了上述字符串中的构造函数名。例如

    var value=[1,2,3,4,5]

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

    由此,我们可以创建函数来判断数据类型:

    //示例
    switch(Object.prototype.toString.call(value)){
      case "[object Array]":
        alert('array');  
        break;
      case "[object Function]":
        alert("function");
        break;
      case "[object RegExp]":
        alert("regexp");
        break;
      case "[object JSON]":
        alert("json")
        break
    }

    二。作用域安全的构造函数:

    我们来看下面的一个实例

    function Person(name,age){

       this.name=name;

       this.age=age

    }

    //根据构造函数创建实例(正常情况应该是这样:var person=new Person("jame",29))

    var person=Person("jame",29)

    alert(person.name)  //js报错,未定义

    alert(person.age)  //js报错,未定义

    alert(window.name) //jame

    由上可见,当我们没有使用new操作符调用构造函数Person时,this会映射到对象window上。这是由于this对象的晚绑定造成的,this对象是在运行时绑定的,所以直接调用Person(),构造函数座位普通函数调用,导致错误的发生。而且由于window的name属性是识别链接目标和frame的,所以这里对该属性的偶然覆盖可能会导致该页面上出现其他错误。这个问题的解决办法就是创建一个作用域安全的构造函数。如下:

    function Person(name,age){
        if(this instanceof Person){ //检测this对象是否为正确类型的实例,如若不是,那么创建新的对象并返回
            this.name=name;
            this.age=age;            
        }else{
            return new Person(name,age)
        } 
    }
    var person1=new Person("jame",28);
    alert(person1.name);  //jame
    alert(person1.age);   //28
    alert(window.name)    //''
    alert(window.age)     //undefined

    ps:实现这个模式后,同时就锁定了可以调用构造函数的环境,如果你使用构造函数窃取模式的继承且不使用原型链,那么这个继承很可能被破坏,如下:

     1 function Polygon(sides){
     2    if(this instanceof Polygon){
     3       this.sides=sides;
     4       this.getArea=function(){
     5          return 0;
     6       }
     7    }else{
     8        return new Polygon(sides);
     9    }
    10 }
    11 function rect(w,h){
    12   Polygon.call(this,2);
    13   this.width=w;
    14   this.height=h;
    15   this.getArea=function(){
    16     return this.width*this.height  
    17   }    
    18 }
    19 var rect1=new rect(5,10)
    20 alert(rect.sides)  //undefined

    按照以往理解,rect.sides的结果应为2.因为我们在rect函数的第一行用call方法调用了polygon的方法,可是结果为什么为undefined呢?

    在这段代码中,Polygon构造函数的作用域是安全的,然而rect构造函数却不是。新创建一个rect实例后,这个实例应该通过Polygon。call来继承Polygon的sides属性。但是由于plygon函数的作用域是安全的,this对象并非polygon的实例,所以会创建并返回一个新的polygon对象。rect中的this并没有得到变化,同时polygon.call返回的值也没有用刀,所以rect实例中不会有sides属性。

    解决办法如下:

    function Polygon(sides){
       if(this instanceof Polygon){
          this.sides=sides;
          this.getArea=function(){
             return 0;
          }
       }else{
           return new Polygon(sides);
       }
    }
    function rect(w,h){
      Polygon.call(this,2);
      this.width=w;
      this.height=h;
      this.getArea=function(){
        return this.width*this.height  
      }    
    }
    rect.prototype=new Polygon()
    var rect1=new rect(5,10)
    alert(rect.sides)  //2

    注意代码中加粗的部分,这样一个rect实例同时也是一个Polygon实例,所以Polygon。call会按预定执行。

    三。函数绑定

    函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数座位变量传递的同时保留代码执行环境,请看下面例子:

     1 var handler={
     2      message:"hello world",
     3      handleClick:function(event){
     4         alert(this.message)
     5      }     
     6  }
     7  var btn=document.getElementById("my_btn");
     8  EventUtil.addHandler(btn,"click",handler.handleClick)//跨浏览器事件处理
     9  var EventUtil={
    10      addHandler:function(element,type,handler){
    11        if(element.addEventListener){
    12            element.addEventListener(type,handler,false)
    13        }else if(element.attachEvent){
    14            element.attachEvent("on"+type,handler)  
    15        }else {
    16           element["on"+type]=handler 
    17        }
    18      },
    19      removeHandler:function(element,type,handler){
    20        if(element.removeEventListener){
    21            element.removeEventListener(type,handler,false)
    22        }else if(element.detachEvent){
    23            element.detachEvent("on"+type,handler)  
    24        }else {
    25           element["on"+type]=null 
    26        }
    27      },
    28  }

    在这个例子中,我们对按钮绑定点击事件,当点击发生时,我们期望得到的是”hello world“,可是结果却为undefiend。这个问题在于没有保存handler.handleClick的环境,所以this对象指向Dom按钮而非handler,我们可以用闭包来修正此问题;如下:

     var handler={
         message:"hello world",
         handleClick:function(event){
            alert(this.message)
         }     
     }
     var btn=document.getElementById("my_btn");
     EventUtil.addHandler(btn,"click",function(event){
         handler.handleClick(event);
     })

    这个解决方案在事件处理程序内使用了一个闭包直接调用handler.handleClick()。当然床架你多个闭包可能使代码变得难于理解和调试,因此很多javascript库实现了一个可以函数绑定到制定环境的函数,一般叫bind();一个简单的bind函数接手一个函数和环境,并返回一个在给定环境中调用给定函数的函数,并将所有参数是原封不动传递过去。语法如下:

    function bind(fn,context){

       return function(){

        return fn.apply(context,arguments)

      }

    }

    在bind中创建了闭包,闭包使用apply()调用传入的函数,并给apply()传递context对象和参数(参数是内部函数的并非bind()的)。当调用返回的函数时,它会在给定环境中执行被传入的函数并给出所有参数。在ECMAScript 5中为所有函数定义了一个月uansheng的bind()方法,与上面bind()方法类似,都是要传入作为this值得对象。支持原生bind()方法的浏览器有IE9+,Firefox 4+,Chrome。只要是将某个函数指针以值得形式进行传递,同时该函数必须在特定环境中执行,被绑定函数的效用就凸显歘来了。然后被绑定函数与普通函数相比有更多的开销,他们需要更多内存,同时也因为多重函数调用稍微慢一点,所以最好只在必要时使用。

    原生调用方法如下:

     var handler={
         message:"hello world",
         handleClick:function(event){
            alert(this.message+":"+event.type)
         }     
     }
     var btn=document.getElementById("my_btn");
     EventUtil.addHandler(btn,"click",handler.handleClick.bind(handler))

    今天就到这里,接下来会研究 惰性载入函数及神秘的函数柯里化。

  • 相关阅读:
    Ubuntu开机等待5分钟的取消方法
    329. 矩阵中的最长递增路径
    关于c语言中NULL的数值是否可以被修改
    #pragam在c++(visual studio 2019)编译器中的使用
    当cpu占有率过高时-sleep(0)的妙用
    inline解析
    一、【pytest实战--Web测试】搭建环境
    用openssl aes256 api实现文件加解密-带例程,兼容openssl enc -aes-256-cbc命令
    kali openvas安装
    C++关于变量初始化的琐记
  • 原文地址:https://www.cnblogs.com/mopagunda/p/4644300.html
Copyright © 2011-2022 走看看