一: 什麼是反射機制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; } });