既然是類,那麼就有抽象類別,具體類,類的繼承,同時,類的成員可以有執行個體成員和靜態成員。下面來看一下prototype是怎麼做到這些的。
先看prototype中的以下的代碼:
複製代碼 代碼如下:var Abstract = new Object();
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]);
}
第一個聲明了一個對象Abstract,Object其實是一個函數,他沒有任何成員,所以是一個空類,所以Abstract也就沒有任何成員。這個暫時不說,後面可以看到這是抽象類別的基礎。先解釋以下這個文法:
function.member=function(){}
在這種情況下,function一般都是已經定義過的,這條語句的作用是給function增加一個靜態成員member,member的內容是等號後面的。如上面第二段代碼Object.extend=……,就是給Object這個類增加了一個靜態方法extend。ok,我們知道了怎樣給一個類定義靜態成員,那麼你一定很想知道執行個體成員怎麼定義,很簡單,在類名和成員名之間加上prototype:
function.prototype.member=function(){}
prototype不僅可以這麼使用,還可以: 複製代碼 代碼如下:function.prototype={
member1:function(){……},
member2:"abc",
member3:function(){……}
}
這樣就是實現了執行個體成員的定義。但prototype代表什麼意思呢?在第一篇我說過,直接用{}括起來,表示一個對象,如Prototype,Class都是這樣定義的全域對象。而看下面一種用法,prototype後面就是一個{}的結構,難道它也是對象?是的,沒錯,prototype其實也是一個對象!在javascript裡,一個對象我們可以任意增加它的成員,用如下的文法:
object.member=function(){……};
只要經過這樣的定義,一個對象就可以立刻具有member這個方法!javascript就是這麼神奇!
好,我們現在知道了prototype是一個對象,而function是一個函數或者類,那麼我們可以認為prototype是任何一個類(函數)都內部保留的一個靜態成員。它的功能就是儲存這個類的所有成員指標,但這些成員都只是原型,沒有經過初始化,這也符合prototype的原義。你可以隨時通過prototype這個對象來擴充成員。在new一個類時,prototype的成員就經過初始化,然後賦給了執行個體化的對象。
上面第三段代碼Object.prototype.extend=……,就是給Object增加了一個執行個體方法extend,執行個體方法中就可以引用this指標,指向由這個類執行個體化的對象本身。當然,這個對象就具有成員extend。
繼續之前,先瞭解一下兩個語句:
for(var p in object){}
method.apply(object,arguments);
第一句:列舉出一個變數的所有成員,如果是函數,那麼是所有靜態成員;如果是對象,那就是所有執行個體成員,p的類型是一個字串。表示成員的名稱。引用一個成員不僅可以用variabel.member,還可以用variabel["member"]。反過來,賦值也是如此。這就給枚舉一個變數的成員帶來了很大方便。
第二條語句:將method這個方法應用到object去執行,參數是arguments這個數組。注意:method並不是object的成員。但是,我們可以認為這條語句執行的意思就是:object.method(arguments)。這是一個很重要的方法,後面會經常用到,你也會逐漸熟悉它的。
下面繼續extend,它是一個非常重要的方法,可以看到它既是類Object的靜態成員,也是其執行個體成員,那它有什麼作用呢?讓我們來看:它接收兩個參數,destination和source,如果destination和source都是類,那麼它的功能是把類source的所有靜態成員都複製給類destination,如果destination和source都是對象,那麼是把所有執行個體成員都複製過來。這時destination中如果已經有同名成員,那麼這個成員將被覆蓋。也就是說讓destination具有了source的所有成員,並且函數返回這個destination。下面看extend作為Object的執行個體成員:
Object.prototype.extend = function(object) {
return Object.extend.apply(this, [this, object]);
}
開始有點暈了,不過不要急,還是可以看懂的,apply文法剛剛已經講過了,它的調用者是一個方法,而Object.extend是一個靜態方法,它被應用到this上面,也就是Object的執行個體,假設為obj,後面方括弧是一個數組,包括兩個成員,this和object。這個數組實際上就是Object靜態成員extend的arguments參數。那麼這條語句就相當於執行
obj.extend(this,object);
this不解釋了,表示本身。object是什嗎?參數,恩,是執行個體方法extend傳來的參數,不要混淆。extend呢?obj並沒有定義extend執行個體成員,但通過apply,它可以把Object的靜態成員extend拿來使用,再看一下extend的函數體:
Object.extend = function(destination, source) {
for (property in source) {
destination[property] = source[property];
}
return destination;
}
因為obj是對象,object也是對象,即destination和source都是對象,於是函數的作用就是使obj具有object的所有成員。並且會返回obj。聽起來有點拗口,但邏輯很簡單:讓obj“繼承於”object!很好,我們看到了繼承,但你肯定會問,對象的繼承,第一次聽說啊,我們講繼承都是講的類的繼承。沒錯,現在的確還沒有看到真正的類繼承,但已經近在眼前了:類不就是有個prototype嗎,而prototype是對象!
好,想到這一點,類的繼承文法看似很簡單了:
b.prototype.extend(a.prototype);
讓b繼承a。
可是事實卻沒那麼簡單:prototype是存放方法原型指標,extend方法沒有初始化,不能使用!要使用extend,就必須執行個體化一個對象。還是看看prototype是怎麼做的吧:
b.prototype=(new a()).extend(b.prototype);
很高明的辦法!充分說明了函數其實也是一個變數的道理。先執行個體化a對象,然後在它基礎上調用extend,將所有的成員b.prototype的成員覆蓋到a的對象,然後把這個a對象再賦值給b.prototype。完成了b從a繼承的工作。在實際使用中,一般的用法都是:
b.prototype=(new a()).extend({});
因為讓一個b繼承自a,通常b之前都是一個未定義的類,所以後面的{}中其實就可以定義類成員。當然,你也可以先定義,再繼承,只是和傳統概念有所區別了。
OK,今天寫到這裡很累了,估計看的人也是,呵呵。現在我們基本明白了prototype的類開發架構,可以看一些進階應用程式了,下回合再見:) 複製代碼 代碼如下:<script language="javascript">
//給Object對象增加靜態方法extend,該方法的作為複製source有所有屬性和方法到destination
Object.extend = function(destination, source) {
for (property in source) {
destination[property] = source[property];
}
return destination;
}
var dog = function(name)
{
this.name = name;
}
//將printName方法複製給dog.prototype
Object.extend(dog.prototype,
{
printName:function()
{
alert(this.name);
}
}
);
var a = new dog("dog");
a.printName();
</script>