理解JavaScript物件導向的思路

來源:互聯網
上載者:User
一般來說大家比較熟悉的物件導向方式是基於類的物件導向,聲明一個類,然後在根據類聲明的描述去建立對象,通過類與類之間的繼承和組合關係來複用代碼。大多數情況下,基於類的物件導向語言(C++,C#,Java之類的)都把類整合進自己的類型系統,即每個類(Class)同時也是一個變數類型(Variable Type),並允許子類類型的值被賦值給父類類型變數。

而JS的設計採用了一種完全不同的思路。首先JS的類型是不可擴充的(就是說,語言的使用者無法添加新的類型)這樣就無法採用上述語言的做法。根據語言標準,JS設計了6種使用者可以使用的資料類型(因為JS是弱類型的,所以變數沒有類型,只有資料有類型):
Boolean Number String Null Undefined Object

為了實現物件導向,JS把所有的對象放到Object類型中,這樣,JS就有6種使用者可使用的資料類型。除了Undefined,JS為所有的類型提供了字面值(literal)文法,現在來看,JS的Object字面值表示設計的相當成功,現在甚至成為了一種資料交換的格式,這就是大家所熟悉的JSON。A Sample:var aTShirt={color:"yellow",size:"big"} 

作為動態語言,JS允許使用者對一個已經建立的對象添加或者刪除屬性。對一個不存在的屬性賦值即向其添加屬性,delete關鍵字被用於刪除屬性。這個delete比較容易跟C++的delete運算子混淆,後者是用來釋放不再使用的對象的。

本來有了這些文法,已經可以做基本的物件導向編程了,但是僅僅如此,JS代碼複用性比其它語言弱太多。比如,你甚至無法為一組對象做一個統一的操作,必須通過迴圈遍曆來實現,於是JS引入了原型(prototype),具體的實現方式是為每個對象規定一個私人屬性[[prototype]],當讀取一個對象的屬性時,如果對象本身沒有這個屬性,會嘗試訪問[[prototype]]的相應屬性。具體實現中,[[prototype]]所指向的對象仍然可以有[[prototype]],實際的訪問就是一個鏈式的操作,直到找到這個屬性或者[[prototype]]為空白為止,所以常常聽到[[prototype]]鏈的說法。為了防止[[prototype]]出現迴圈,JS引擎會在任何對象的[[prototype]]屬性被修改時檢查。

按照標準,這個[[prototype]]語言使用者是無法訪問的,不過FireFox的JS引擎把[[prototype]]暴露出來,作為公有屬性"__proto__",這樣,我們就可以通過操作原型對象來控制一組對象的行為。我們可以借用FF提供的便利來瞭解一下[[prototype]]的工作原理:

var proto={a:1};
var m={__proto__:proto};
var n={__proto__:proto};
alert([m.a,n.a]);
proto.a=2;
alert([m.a,n.a]);

JS規定了一個內建對象作為所有對象的最終[[prototype]],也就是說即使用{}建立的對象,也會有[[prototype]]指向這個內建對象。

通過這個機制,我們完全可以得到跟基於類的語言相當程度的對象複用能力——但是當然我們還需要函數。在JS中,函數僅僅是一種特殊的對象,JS設計了()運算子和function關鍵字讓JS的函數看起來更像是傳統的語言。只要實現了私人方法[[call]]的對象都被認為是函數對象(這個[[call]]跟大家比較熟悉的Function.prototype.call完全是兩回事),類似[[prototype]],[[call]]也是語言使用者完全無法訪問的,這一次FF也沒有為我們提供公有屬性來替代。

本來到這裡為止,JS的物件導向已經很完整了,但是JS為了讓自己的文法看起來更像是Java之類的語言,又引入了new關鍵字,在上面大部分語言中new都是針對類來做的,而JS沒有類,甚至沒有聲明域,所以這個new還是要在對象上做文章,new會調用私人方法[[contruct]],任何實現了[[construct]]的對象都可以被new接受。然而如何才能讓一個對象可以被new呢?JS並沒有額外提供構造這種對象方法,所以所有通過function關鍵字構造的函數對象被設計成實現了[[construct]]方法。這也就是JS的new很奇怪地針對函數的原因。值得一提的是,並非只有函數可以被new,JS的宿主環境可能提供一些其它對象,典型的例子是IE中的ActiveXObject。

所有函數的[[construct]]方法都是類似的:建立一個新的對象,將它的[[prototype]]設為函數對象的共有屬性prototype,以新對象做為this指標的值,執行函數對象

這樣對同一函數的new運算實際上建立了相似的對象:擁有共同的原型[[prototype]],被同一函數處理過。這樣的new運算就很類似Class了,同時由於JS的動態性,所有的"類"在運行時任你宰割,想要類比繼承之類的行為也就很容易了,由於是弱類型且是動態函數,不存在需要多態的問題,JS完全可以做到基於類的物件導向。

最後提供幾道題,大家茶餘飯後寫完程式不妨做做,都做對說明你已經理解了protype-based javascript

(請用FF來看結果)

Function.prototype.prop=1;
alert(Object.prop)
alert(Function.prop)
Object.prototype.prop=1;
alert(Object.prop)
alert(Function.prop)Function.__proto__.prop=1;
alert(Object.prop)
alert(Function.prop)function Class(){
}
Class.prototype=Class;
Class.__proto__.prop=1
alert((new Class).prop);function Class(){}
Class.prototype=Class.__proto__;
Function.prototype.prop=1;
alert((new Class()).prop)function Class(){
}
Class.prototype.__proto__.prop=1;
Class.prototype=new Class;
alert((new Class).prop);
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.