Javascript中的反射機制(五)

一: 什麼是反射機制javascript

      反射機制指的是程序在運行時可以獲取自身的信息。例如一個對象可以在運行時知道本身有哪些方法和屬性。html

二: 在JavaScript中利用for(…in…)語句實現反射java

在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程序設計中,反射機制是很重要的一種技術,它在實現類的繼承中發揮了很大的做用。sql

 

反射的一個很經典實例:編程

<!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中僅改變指定的樣式,而其餘樣式不會改變,獲得了所要的結果。app

<!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;
    }
});
相關文章
相關標籤/搜索