zoukankan      html  css  js  c++  java
  • Javascript中的反射机制(五)

    一: 什么是反射机制

          反射机制指的是程序在运行时能够获取自身的信息。例如一个对象能够在运行时知道自己有哪些方法和属性。

    二: 在JavaScript中利用for(…in…)语句实现反射

    在JavaScript中有一个很方便的语法来实现反射,即for(…in…)语句,其语法如下:
    for(var p in obj){
          //语句
    }
    这里var p表示声明的一个变量,用以存储对象obj的属性(方法)名称,有了对象名和属性(方法)名,就可以使用方括号语法来调用一个对象的属性(方法):
    for(var p in obj){
          if(typeof(obj[p])=="function"){
                 obj[p]();
          }else{
                 alert(obj[p]);
          }
    }
    这段语句遍历obj对象的所有属性和方法,遇到属性则弹出它的值,遇到方法则立刻执行。在面向对象的JavaScript程序设计中,反射机制是很重要的一种技术,它在实现类的继承中发挥了很大的作用。

    反射的一个很经典实例:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title></title>
        <script language="JavaScript" type="text/javascript">
       function Person(){
           this.personName="孙丽媛";
           this.personAge =18;
           this.showInfo=function(){
               alert(this.personName+ "," + this.personAge);
           }
       }
       
    
            function show(){
                var obj=new Person();
                for(var o in obj)
                {
                    if(typeof(obj[o])=="function"){
                        var isOk=confirm(o+"是一个方法,是否需要查看源码");
                        if(isOk){
                            alert(obj[o]);
                        }
                    }else{
                        alert(o);
                    }
                }
            }
        </script>
    </head>
    <body>
    
    <button onclick="show()">test</button>
    
    
    </body>
    </html>
    

     三: 使用反射来传递样式参数

    在Ajax编程中,经常要能动态的改变界面元素的样式,这可以通过对象的style属性来改变,比如要改变背景色为红色,可以这样写:
    element.style.backgroundColor="#ff0000";
    其中style对象有很多属性,基本上CSS里拥有的属性在JavaScript中都能够使用。如果一个函数接收参数用用指定一个界面元素的样式,显然一个或几个参数是不能符合要求的,下面是一种实现:
    function setStyle(_style){
          //得到要改变样式的界面对象
          var element=getElement();
          element.style=_style;
    }
    这样,直接将整个style对象作为参数传递了进来,一个style对象可能的形式是:
    var style={
          color:#ffffff,
          backgroundColor:#ff0000,
          borderWidth:2px
    }
    这时可以这样调用函数:
    setStyle(style);
    或者直接写为:
    setStyle({ color:#ffffff,backgroundColor:#ff0000,borderWidth:2px});
    这段代码看上去没有任何问题,但实际上,在setStyle函数内部使用参数_style为element.style赋值时,如果element原先已经有了一定的样式,例如曾经执行过:
    element.style.height="20px";
    而_style中却没有包括对height的定义,因此element的height样式就丢失了,不是最初所要的结果。要解决这个问题,可以用反射机制来重写setStyle函数:
    function setStyle(_style){
          //得到要改变样式的界面对象
          var element=getElement();
          for(var p in _style){
                element.style[p]=_style[p];
          }
    }
    程序中遍历_style的每个属性,得到属性名称,然后再使用方括号语法将element.style中的对应的属性赋值为_style中的相应属性的值。从而,element中仅改变指定的样式,而其他样式不会改变,得到了所要的结果。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title></title>
        <script language="JavaScript" type="text/javascript">
      function changeDivStyle() {
                var oDiv = document.getElementById("mydiv");
                var oStyle = {
                    backgroundColor: "#ff0000",
                    color: "#ffffff"
                }
                if (oDiv) {
                    for (var o in oStyle) {
                        oDiv.style[o] = oStyle[o];
                    }
                }
            }
        </script>
    </head>
    <body>
    
    <div id="mydiv" style="200px;height: 200px">
    sunliyuan
    </div>
    <button onclick='changeDivStyle({backgroundColor:"#ff0000",color:"#ffffff","text-align":"center"})'>ChangeDivStyle</button>
    
    </body>
    </html>
    

      四:利用共享prototype实现继承

       继承是面向对象开发的又一个重要概念,它可以将现实生活的概念对应到程序逻辑中。例如水果是一个类,具有一些公共的性质;而苹果也是一类,但它们属于水果,所以苹果应该继承于水果。
    在JavaScript中没有专门的机制来实现类的继承,但可以通过拷贝一个类的prototype到另外一个类来实现继承。一种简单的实现如下:
    fucntion class1(){
          //构造函数
    }
    function class2(){
          //构造函数
    }
    class2.prototype=class1.prototype;
    class2.prototype.moreProperty1="xxx";
    class2.prototype.moreMethod1=function(){
          //方法实现代码
    }
    var obj=new class2();
    这样,首先是class2具有了和class1一样的prototype,不考虑构造函数,两个类是等价的。随后,又通过prototype给class2赋予了两个额外的方法。所以class2是在class1的基础上增加了属性和方法,这就实现了类的继承。
    JavaScript提供了instanceof操作符来判断一个对象是否是某个类的实例,对于上面创建的obj对象,下面两条语句都是成立的:
    obj instanceof class1
    obj instanceof class2
    表面上看,上面的实现完全可行,JavaScript也能够正确的理解这种继承关系,obj同时是class1和class2的实例。事是上不对, JavaScript的这种理解实际上是基于一种很简单的策略。先使用prototype让class2继承于class1,再在 class2中重复定义method方法:
    <script language="JavaScript" type="text/javascript">
    <!--
    //定义class1
    function class1(){
          //构造函数
    }
    //定义class1的成员
    class1.prototype={
          method:function(){
                alert(1);
          }
    }
    //定义class2
    function class2(){
          //构造函数
    }
    //让class2继承于class1
    class2.prototype=class1.prototype;
    //给class2重复定义方法method
    class2.prototype.method=function(){
          alert(2);
    }
    //创建两个类的实例
    var obj1=new class1();
    var obj2=new class2();
    //分别调用两个对象的method方法
    obj1.method();
    obj2.method();
    //-->
    </script>
    从代码执行结果看,弹出了两次对话框“2”,当对class2进行prototype的改变时,class1的prototype也随之改变,即使对class2的prototype增减一些成员,class1的成员也随之改变。所以class1和class2仅仅是构造函数不同的两个类,它们保持着相同的成员定义。class1和class2的prototype是完全相同的,是对同一个对象的引用。其实从这条赋值语句就可以看出来:
    //让class2继承于class1
    class2.prototype=class1.prototype;
    在JavaScript 中,除了基本的数据类型(数字、字符串、布尔等),所有的赋值以及函数参数都是引用传递,而不是值传递。所以上面的语句仅仅是让class2的 prototype对象引用class1的prototype,造成了类成员定义始终保持一致的效果。从这里也看到了instanceof操作符的执行机制,它就是判断一个对象是否是一个prototype的实例,因为这里的obj1和obj2都是对应于同一个prototype,所以它们 instanceof的结果都是相同的。
    因此,使用prototype引用拷贝实现继承不是一种正确的办法。但在要求不严格的情况下,却也是一种合理的方法,惟一的约束是不允许类成员的覆盖定义。

    五: 利用反射机制和prototype实现继承

         共享prototype来实现类的继承,不是一种很好的方法,毕竟两个类是共享的一个prototype,任何对成员的重定义都会互相影响,不是严格意义的继承。但在这个思想的基础上,可以利用反射机制来实现类的继承,思路如下:利用for(…in…)语句枚举出所有基类prototype的成员,并将其赋值给子类的prototype对象。例如:
    <script language="JavaScript" type="text/javascript">
    <!--
    function class1(){
          //构造函数
    }
    class1.prototype={
          method:function(){
               alert(1);
          },
          method2:function(){
               alert("method2");
          }
    }
    function class2(){
          //构造函数
    }
    //让class2继承于class1
    for(var p in class1.prototype){
           class2.prototype[p]=class1.prototype[p];
    }
    //覆盖定义class1中的method方法
    class2.prototype.method=function(){
          alert(2);
    }
    //创建两个类的实例
    var obj1=new class1();
    var obj2=new class2();
    //分别调用obj1和obj2的method方法
    obj1.method();
    obj2.method();
    //分别调用obj1和obj2的method2方法
    obj1.method2();
    obj2.method2();
    //-->
    </script>
    从运行结果可见,obj2中重复定义的method已经覆盖了继承的method方法,同时method2方法未受影响。而且obj1中的method方法仍然保持了原有的定义。这样,就实现了正确意义的类的继承。为了方便开发,可以为每个类添加一个共有的方法,用以实现类的继承:
    //为类添加静态方法inherit表示继承于某类
    Function.prototype.inherit=function(baseClass){
         for(var p in baseClass.prototype){
                this.prototype[p]=baseClass.prototype[p];
         }
    }
    这里使用所有函数对象(类)的共同类Function来添加继承方法,这样所有的类都会有一个inherit方法,用以实现继承。于是,上面代码中的:
    //让class2继承于class1
    for(var p in class1.prototype){
           class2.prototype[p]=class1.prototype[p];
    }
    可以改写为:
    //让class2继承于class1
    class2.inherit(class1)
    这样代码逻辑变的更加清楚,也更容易理解。通过这种方法实现的继承,有一个缺点,就是在class2中添加类成员定义时,不能给prototype直接赋值,而只能对其属性进行赋值,例如不能写为:
    class2.prototype={
          //成员定义
    }
    而只能写为:
    class2.prototype.propertyName=someValue;
    class2.prototype.methodName=function(){
          //语句
    }
    由此可见,这样实现继承仍然要以牺牲一定的代码可读性为代价,(注:prototype-1.3.1框架是一个JavaScript类库,扩展了基本对象功能,并提供了实用工具。)中实现的类的继承机制,不仅基类可以用对象直接赋值给 property,而且在派生类中也可以同样实现,使代码逻辑更加清晰,也更能体现面向对象的语言特点。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title></title>
        <script language="JavaScript" type="text/javascript">
      function class1() {
      }
            class1.prototype={
             p1:"p1",
                method1:function(){
                    alert("from class1 method1");
                }
            }
    function class2(){
    }
            class2.prototype=class1.prototype;
            class2.prototype.p2="p2";
            class2.prototype.method1=function(){
              alert("from class2 method1");
          };
    
      class2.prototype.method2 =function(){
          alert("method2");
      };
    
    
      var cls1 = new class1();
      alert(cls1.p1);
      cls1.method1();
    
      var cls2 = new class2();
      alert(cls2.p1);
      alert(cls2.p2);
      cls2.method1();
      cls2.method2();
    
    
        </script>
    </head>
    <body>
    
    <div id="mydiv" style="200px;height: 200px">
    
    </div>
    
    </body>
    </html>
    

     用 inheritjs 代码:

    Function.prototype.inherit=function(baseClass){
        for(var p in baseClass.prototype){
            this.prototype[p]=baseClass.prototype[p];
        }
    }
    

    五:prototype-1.3.1框架自实现JS中的类的继承

    在prototype-1.3.1框架中,首先为每个对象都定义了一个extend方法:
    //为Object类添加静态方法:extend
    Object.extend = function(destination, source) {
      for(property in source) {
         destination[property] = source[property];
      }
      return destination;
    }
    //通过Object类为每个对象添加方法extend
    Object.prototype.extend = function(object) {
      return Object.extend.apply(this, [this, object]);
    }
    Object.extend 方法很容易理解,它是Object类的一个静态方法,用于将参数中source的所有属性都赋值到destination对象中,并返回 destination的引用。Object.prototype.extend的实现,因为Object是所有对象的基类,所以这里是为所有的对象都添加一个extend方法,函数体中的语句如下:
    Object.extend.apply(this,[this,object]);
    这一句是将Object类的静态方法作为对象的方法运行,第一个参数this是指向对象实例自身;第二个参数是一个数组,包括两个元素:对象本身和传进来的对象参数object。函数功能是将参数对象object的所有属性和方法赋值给调用该方法的对象自身,并返回自身的引用。有了这个方法,下面看类继承的实现:
    <script language="JavaScript" type="text/javascript">
    <!--
    //定义extend方法
    Object.extend = function(destination, source) {
      for (property in source) {
         destination[property] = source[property];
      }
      return destination;
    }
    Object.prototype.extend = function(object) {
      return Object.extend.apply(this, [this, object]);
    }


    //定义class1
    function class1(){
          //构造函数
    }
    //定义类class1的成员
    class1.prototype={
          method:function(){
               alert("class1");
          },
          method2:function(){
               alert("method2");
          }
    }
    //定义class2
    function class2(){
          //构造函数
    }
    //让class2继承于class1并定义新成员
    class2.prototype=(new class1()).extend({
          method:function(){
               alert("class2");
          }
    });
    //创建两个实例
    var obj1=new class1();
    var obj2=new class2();
    //试验obj1和obj2的方法
    obj1.method();
    obj2.method();
    obj1.method2();
    obj2.method2();
    //-->
    </script>
    从运行结果可以看出,继承被正确的实现了,而且派生类的额外成员也可以以列表的形式加以定义,提高了代码的可读性。下面解释继承的实现:
    //让class2继承于class1并定义新成员
    class2.prototype=(new class1()).extend({
          method:function(){
               alert("class2");
          }
    });
    上段代码也可以写为:
    //让class2继承于class1并定义新成员
    class2.prototype=class1.prototype.extend({
          method:function(){
               alert("class2");
          }
    });
    但因为extend方法会改变调用该方法对象本身,所以上述调用会改变class1的prototype的值,在 prototype-1.3.1框架中,巧妙的利用new class1()来创建一个实例对象,并将实例对象的成员赋值给class2的prototype。其本质相当于创建了class1的prototype 的一个拷贝,在这个拷贝上进行操作自然不会影响原有类中prototype的定义了。

    整个代码如下:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title></title>
        <title></title>
        <script type="text/javascript" src="js/Core.js"></script>
        <script type="text/javascript">
            function Class01(){
    
            }
            Class01.prototype={
                p1:"p1",
                method01:function(){
                    alert("from class01 method01");
                }
            };
    
            function Class02(){
    
            }
            Class02.prototype =(new Class01()).extend({
                p2:"p2",
                method02:function(){
                    alert("method02");
                },
                method03:function(){
                    alert("method03");
                },
                method01:function(){
                    alert("from class02 method01");
                }
            });
    
            var class01 = new Class01();
            alert(class01.p1);
            class01.method01();
    
            var class02 = new Class02();
            alert(class02.p1);
            alert(class02.p2);
            class02.method01();
            class02.method02();
    
        </script>
    </head>
    <body>
    
    <div id="mydiv" style="200px;height: 200px">
    
    </div>
    
    </body>
    </html>
    

     Core.js代码:

    Function.prototype.inherit=function(baseClass){
        for(var p in baseClass.prototype){
            this.prototype[p]=baseClass.prototype[p];
        }
    }
    Object.extend = function(destination, source) {
        for (property in source) {
            destination[property] = source[property];
        }
        return destination;
    }
    Object.prototype.extend = function(object) {
        return Object.extend.apply(this, [this, object]);
    }
    

    六:Prototype.js源码解析

      参考:http://www.blogjava.net/TrampEagle/articles/30261.html

    七:Prototype.js中类的继承实现示例

    参考:  http://www.108js.com/article/article6/60025.html?id=722

    具体的代码如下:

    HTML页:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title></title>
        <script type="text/javascript" src="js/prototype-1.6.0.3.js"></script>
        <script type="text/javascript" src="js/Person.js"></script>
        <script type="text/javascript" src="js/Employee.js"></script>
    <script type="text/javascript">
        //创建一个类
       function getEmployeeInfo(){
           var employee = new Employee();
           employee.personName="wangwu";
           employee.personAge=28;
           employee.corpName="Google";
    
           var info = employee.showInfo();
    
           alert(info);
       }
    
    </script>
    </head>
    <body>
    <button onclick="getEmployeeInfo()">GetEmployeeInfo</button>
    </body>
    </html>
    

     本身Js代码:

    var Person = Class.create();
    Person.prototype={
        //必须给初始化值
        initialize: function() {
        },
        personName:"zhang",
        personAge:18,
        showInfo:function(){
            alert(this.personName+","+this.personAge);
        }
    }
    

     继承的js 代码:

    var Employee = Class.create();
    
    Employee.prototype = Object.extend(new Person(), {
        initialize: function() {
        },
        corpName:"Micosoft",
        showInfo:function(){
            return this.personName+","+this.personAge+","+this.corpName;
        }
    });
    
  • 相关阅读:
    网页加速的14条优化法则 网站开发与优化
    .NET在后置代码中输入JS提示语句(背景不会变白)
    C语言变量声明内存分配
    SQL Server Hosting Toolkit
    An established connection was aborted by the software in your host machine
    C语言程序设计 2009春季考试时间和地点
    C语言程序设计 函数递归调用示例
    让.Net 程序脱离.net framework框架运行
    C语言程序设计 答疑安排(2009春季 110周) 有变动
    软件测试技术,软件项目管理 实验时间安排 2009春季
  • 原文地址:https://www.cnblogs.com/sunliyuan/p/5861828.html
Copyright © 2011-2022 走看看