生活随笔
收集整理的這篇文章主要介紹了
在Javascript中使用面向对象的编程
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
by Mike Koss
March 26th, 2003
這是一篇,我個人認為最好的,Javascript面向?qū)ο缶幊痰奈恼隆7g不好的地方,還望大家指正,謝謝。
如果您需要,可以訪問下面的地址取得原文:
http://mckoss.com/jscript/object.htm
在我的blog里,將會陸續(xù)推出這個理論的實踐、源碼。
介紹
大部分的Javascript的編寫者,都只是把它做為簡單的腳本引擎,來創(chuàng)建動態(tài)的Web頁面。同時Web設(shè)計人員開始使用在IE瀏覽器中定義的對象模型,來處理Web頁面的內(nèi)容。但是大多數(shù)的開發(fā)者并沒有認識到Javascript在其自身就具有強大的面向?qū)ο蟮墓δ堋.?dāng)不使用強類型的時候(變量不必先聲明后使用),這種解析性的語言,可以巧妙的達成面向?qū)ο?#xff08;object-oriented)的功能,包括:
- 封裝?(Encapsulation)
- 多臺?(Polymorphism )
- 繼承?(Inheritance)
雖然,通過一系列的范例(對于好奇的讀者,這些范例片斷代碼是很生動的),我將會闡述對象在Javascript中,對象是如何被使用,并且如何實現(xiàn)面向?qū)ο蟮摹?/p>
簡單對象(Simple Objects)
在Javascript中,最簡單的可構(gòu)建的對象,就是機制內(nèi)建的Object對象。在Javascript中,對象是指定名稱的屬性(property)的集合。做為解析性語言,Javascript允許給一個對象創(chuàng)建任意個屬性,在任何時間(不像C++,它的屬性是可以在任何時間添加給對象。它們并不需要事先在對象的聲明(definition)或者構(gòu)造(constructor)中,進行定義)。
所以,舉例來說,我們可以創(chuàng)建一個對象,然后添加一系列的屬性給它,就像這樣:
obj = new Object;<br><br>obj.x = 1;<br><br>obj.y = 2;
這里,Javascript對象,可以用圖形表示成這樣的結(jié)構(gòu):
| obj |
| x | 1 |
| y | 2 |
| prototype properties |
| constructor | function?Object |
另外需要注意的是,我們創(chuàng)建的x和y屬性, 我們的對象默認有一個屬性constructor t他指向一個Javascript內(nèi)部對象函數(shù)(funciton)。 (譯者注:prototype,原型在后文會有進一步的說明)
對象的構(gòu)造函數(shù)(Object Constructors)
對于要定義的對象類型,Javascript允許我們自己給對象類型定義構(gòu)造函數(shù):
function Foo()<br><br>{<br><br> this.x = 1;<br><br> this.y = 2;<br><br>}<br><br> <br><br>obj1 = new Foo;<br><br>| obj1 |
| x | 1 |
| y | 2 |
| prototype properties |
| constructor | function?Foo |
這里要說明的是,我們可以創(chuàng)建任意多個Foo類型的對象實例,它們也都將分別初始化自己的x和y屬性為1和2。
簡單的方法的的實現(xiàn)(A Simple Method Implementation)
為了封裝對象的行為功能,向調(diào)用者隱藏執(zhí)行過程,我們需要給對象創(chuàng)建方法(method)。Javascript允許你將任意一個函數(shù)(function)分配給對象的一個屬性。當(dāng)我們使用 obj.Function 的語法調(diào)用函數(shù)的時候,將把函數(shù)原來定義this 的指向,當(dāng)前這個對象(就像它在構(gòu)造函數(shù)中的那樣)。
function Foo()<br><br>{<br><br> this.x = 1;<br><br> this.y = 2;<br><br> this.Bar = MyMethod;<br><br>}<br><br> <br><br>function MyMethod(z)<br><br>{<br><br> this.x += z;<br><br>}<br><br> <br><br>obj2 = new Foo;<br><br>| obj2 |
| x | 1 |
| y | 2 |
| Bar | function?MyMethod |
| prototype properties |
| constructor | function?Foo |
現(xiàn)在,我們簡單的調(diào)用一下,做為對象的方法的Bar函數(shù):
obj2.Bar(3);<br><br>| obj2 |
| x | 4 |
| y | 2 |
| Bar | function?MyMethod |
| prototype properties |
| constructor | function?Foo |
所以,你可以方便的給對象定義構(gòu)造函數(shù)和方法,使其對調(diào)用者而言,隱藏它的實現(xiàn)過程。同樣的,因為,Javascript不是強類型的,所以,我們可以通過定義有相同名字的方法的對象,來簡單的實現(xiàn)多臺性(polymorphism)。
function Foo()<br><br>{<br><br> this.x = 1;<br><br> this.DoIt = FooMethod;<br><br>}<br><br> <br><br>function FooMethod()<br><br>{<br><br> this.x++;<br><br>}<br><br> <br><br>function Bar()<br><br>{<br><br> this.z = 'Hello';<br><br> this.DoIt = BarMethod;<br><br>}<br><br> <br><br>function BarMethod()<br><br>{<br><br> this.z += this.z;<br><br>}<br><br> <br><br>obj1 = new Foo;<br><br>obj2 = new Bar;<br><br>| obj1 |
| x | 1 |
| DoIt | function?FooMethod |
| prototype properties |
| constructor | function?Foo |
| obj2 |
| z | Hello |
| DoIt | function?BarMethod |
| prototype properties |
| constructor | function?Bar |
function Poly(obj)<br><br>{<br><br> obj.DoIt();<br><br>}<br><br> <br><br>Poly(obj1);<br><br>Poly(obj2);<br><br>| obj1 |
| x | 2 |
| DoIt | function?FooMethod |
| prototype properties |
| constructor | function?Foo |
| obj2 |
| z | HelloHello |
| DoIt | function?BarMethod |
| prototype properties |
| constructor | function?Bar |
使用原型實現(xiàn)方法(Using Prototypes to Implement Methods)
試想一下,這使很笨的辦法,每次我們都要創(chuàng)建名稱沒有使用意義的方法函數(shù),然后在構(gòu)造函數(shù)里,把它們分配給每個方法屬性。其實,我發(fā)現(xiàn)使用Javascript的原型(prototype)機制,是更為直接的方法。?
每個對象,可以參照一個原型對象,原型對象包含有自己的屬性。它就好比是一個對象定義的備份。當(dāng)代碼,引用一個屬性的時候,它并不存在于對象本身里,那么Javascript將會自動的在原型的定義中查找這個屬性。而且,事實上,一個對象的原型對象又可以參照另外一個原型對象,就這樣以鏈?zhǔn)阶罱K關(guān)聯(lián)到基類對象的構(gòu)造函數(shù)。(譯者注:對于DOM對象等系統(tǒng)的對象,原型對象可以修改,但是不可以賦值改變的,只有自定義對象可以。)這是template模型(譯者注:模板方法,《設(shè)計模式》中行為模式的一種),它可以簡化我們對方法的定義,同時也可以產(chǎn)生強大的繼承機制。
在Javascript中,原型對象是被分配給構(gòu)造函數(shù)的。所以,為了修改對象的原型,必須首先修改構(gòu)造函數(shù)的原型對象的成員。然后,當(dāng)對象從構(gòu)造函數(shù)被構(gòu)造的時候,對象將會引用到構(gòu)造函數(shù)的原型。?
function Foo()<br><br>{<br><br> this.x = 1;<br><br>}<br><br> <br><br>Foo.prototype.y = 2;<br><br>obj = new Foo;<br><br>document.write('obj.y = ' + obj.y);<br><br>obj.y = 2| obj |
| x | 1 |
| prototype properties |
| constructor | function?Foo |
| y | 2 |
即使我們并沒有直接的把y屬性分配給obj,obj對象仍然有一個y屬性。當(dāng)我們引用obj.y的時候,Javascript實際返回obj.constructor.prototype.y的引用。我們可以肯定的是,原型的值的改變,也將會反映到對象中。?
Foo.prototype.y = 3;<br><br>document.write('obj.y = ' + obj.y);<br><br>obj.y = 3| obj |
| x | 1 |
| prototype properties |
| constructor | function?Foo |
| y | 3 |
我們也可以發(fā)現(xiàn),一旦我們初始化一個屬性的“私有”(
private )的值,存放在原型中的值并不會收到影響:
obj.y = 4;<br><br>Foo.prototype.y = 3;<br><br>| obj |
| x | 1 |
| y | 4 |
| prototype properties |
| constructor | function?Foo |
原型方法的命名(Prototype Method Naming)
我發(fā)現(xiàn)了可以直接定義類的原型的方法的語句,而不需要單獨的函數(shù)的名稱:
function Foo()<br><br>{<br><br> this.x = 1;<br><br>}<br><br> <br><br>function Foo.prototype.DoIt()<br><br>{<br><br> this.x++;<br><br>}<br><br>obj = new Foo;<br><br>obj.DoIt();| obj |
| x | 2 |
| prototype properties |
| constructor | function?Foo | prototype | | DoIt | function?Foo.prototype.DoIt |
|
| DoIt | function?Foo.prototype.DoIt |
基于原型的子類繼承(Prototype-based Subclassing )
一旦可以建立原型對象鏈,我們就可以使用它做為對象的子類的類型。這個方法要注意的是,我們創(chuàng)建了一個基類對象的實例,并把它做為我們的類的構(gòu)造函數(shù)的原型對象。這么做,我們所創(chuàng)建的所有的對象,將繼承基類對象的所有成員和(方法)。但是要注意,基類的構(gòu)造函數(shù)只會被調(diào)用一次(譯者注:從基類到子類的構(gòu)造函數(shù)都是唯一的,即基類的構(gòu)造函數(shù))。這不像C++,基類的構(gòu)造函數(shù),對于每個繼承的子類,都可以分別的調(diào)用。在后面,我將展示,當(dāng)獨立的構(gòu)造函數(shù)被需要的時候,另外一種可選的方式來創(chuàng)建繼承類。
function TextObject(st)<br><br>{<br><br> this.st = st;<br><br> this.fVisible = true;<br><br>}<br><br> <br><br>function TextObject.prototype.Write()<br><br>{<br><br> document.write('<br><br>' + this.st);<br><br>}<br><br> <br><br>function ItalicTextObject(st)<br><br>{<br><br> this.st = st;<br><br>}<br><br> <br><br>ItalicTextObject.prototype = new TextObject('x');<br><br> <br><br>ItalicTextObject.prototype.Write = ITOWrite;<br><br>function ITOWrite()<br><br>{<br><br> document.write('<br><br><em>' + this.st + '</em>');<br><br>}<br><br> <br><br>obj1 = new TextObject('Hello, mom');<br><br>obj2 = new ItalicTextObject('Hello, world');<br><br>obj1.Write();<br><br>obj2.Write();<br><br>
Hello, mom
Hello, world | obj1 |
| st | Hello,?mom |
| fVisible | true |
| prototype properties |
| constructor | function?TextObject | prototype | | Write | function?TextObject.prototype.Write |
|
| Write | function?TextObject.prototype.Write |
| obj2 |
| st | Hello,?world |
| prototype properties |
| constructor | function?TextObject | prototype | | Write | function?TextObject.prototype.Write |
|
| fVisible | true |
| Write | function?ITOWrite |
這個結(jié)構(gòu)存在兩個問題。一個是,當(dāng)每次構(gòu)造繼承的類的時候,基類的構(gòu)造函數(shù)都不會被調(diào)用。假如,構(gòu)造函數(shù)不做太多的事情,只是初始化一些成員變量為靜態(tài)的值,這個問題就不是太明顯了。第二個,注意,我將不能使用"function Obj.prototype.Method"的方式,來定義繼承類的成員。這是因為,對于構(gòu)造函數(shù)來說,我要把這些方法的定義,放入新創(chuàng)建的原型對象,而不是添加到,默認的原型對象。?
另一種子類繼承方式(An Alternate Subclassing Paradigm)
我們可以提出一種方法,更類似于反映C++類的概念和子類的定義,以及從子類反向存取基類的純原型鏈的風(fēng)格。它需要添加新的方法DeriveFrom給基類。
function Function.prototype.DeriveFrom(fnBase)<br><br><br><br>{<br><br><br><br> var prop;<br><br><br><br><br><br><br><br> if (this == fnBase)<br><br><br><br> {<br><br><br><br> alert("Error - cannot derive from self");<br><br><br><br> return;<br><br><br><br> }<br><br><br><br><br><br><br><br> for (prop in fnBase.prototype)<br><br><br><br> {<br><br><br><br> if (typeof(fnBase.prototype[prop]) == "function" && !this.prototype[prop])<br><br><br><br> {<br><br><br><br> this.prototype[prop] = fnBase.prototype[prop];<br><br><br><br> }<br><br><br><br> }<br><br><br><br><br><br><br><br> this.prototype[fnBase.StName()] = fnBase;<br><br><br><br>}function Function.prototype.StName()<br><br><br><br>{<br><br><br><br> var st;<br><br><br><br><br><br><br><br> st = this.toString();<br><br><br><br> st = st.substring(st.indexOf(" ")+1, st.indexOf("("))<br><br><br><br><br><br><br><br> return st;<br><br><br><br>}function TextObject(st)<br><br>{<br><br> this.st = st;<br><br> this.fVisible = true;<br><br>}<br><br> <br><br>function TextObject.prototype.Write()<br><br>{<br><br> document.write('<br><br>' + this.st);<br><br>}<br><br> <br><br>function TextObject.prototype.IsVisible()<br><br>{<br><br> return this.fVisible;<br><br>}<br><br> <br><br>function ItalicTextObject(st)<br><br>{<br><br> this.TextObject(st);<br><br>}<br><br> <br><br>ItalicTextObject.DeriveFrom(TextObject);<br><br> <br><br>function ItalicTextObject.prototype.Write()<br><br>{<br><br> document.write('<br><br><em>' + this.st + '</em>');<br><br>}<br><br> <br><br>obj1 = new TextObject('Hello, mom');<br><br>obj2 = new ItalicTextObject('Hello, world');<br><br>obj1.Write();<br><br>obj2.Write();<br><br>
Hello, mom
Hello, world | obj1 |
| st | Hello,?mom |
| fVisible | true |
| prototype properties |
| constructor | function?TextObject | prototype | | Write | function?TextObject.prototype.Write | | IsVisible | function?TextObject.prototype.IsVisible |
|
| IsVisible | function?TextObject.prototype.IsVisible |
| Write | function?TextObject.prototype.Write |
| obj2 |
| st | Hello,?world |
| fVisible | true |
| prototype properties |
| constructor | function?ItalicTextObject | prototype | | Write | function?ItalicTextObject.prototype.Write | | IsVisible | function?TextObject.prototype.IsVisible | | TextObject | function?TextObject | prototype | | Write | function?TextObject.prototype.Write | | IsVisible | function?TextObject.prototype.IsVisible |
|
|
| IsVisible | function?TextObject.prototype.IsVisible |
| TextObject | function?TextObject | prototype | | Write | function?TextObject.prototype.Write | | IsVisible | function?TextObject.prototype.IsVisible |
|
| Write | function?ItalicTextObject.prototype.Write |
我們還得到了一個額外的好處,那就是,我們可以從多個基類進行繼承(多重的繼承)。
總結(jié)
以上是生活随笔為你收集整理的在Javascript中使用面向对象的编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。