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="width: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="width: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="width: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;
    }
});

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM