一: 什麼是反射機制
反射機制指的是程式在運作時能夠擷取自身的資訊。例如一個對象能夠在運作時知道自己有哪些方法和屬性。
二: 在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;
}
});