在JS中充分使用物件導向設計思想,可以極大限度的提升代碼重用、降低模組間的偶合、更好的邏輯分層與並行開發。下面分幾個步驟簡單談下我的理解。
一、資料類型與封裝類
封裝類 …… 類型名 …… 常見值 …… 分類
Number …… number …… 123.123 …… 基礎資料型別 (Elementary Data Type)
Boolean …… Boolean …… true、false …… 基礎資料型別 (Elementary Data Type)
String …… string …… “hello world!” …… 基礎資料型別 (Elementary Data Type)
Object …… object …… {}、[] …… 複合資料型別
Function …… function …… function(){} …… 特殊類型
無 …… undefined …… undefined、未定義 …… 小資料類型
無 …… null …… null …… 小資料類型
內建類型與本文關係不大,不列出。
二、參考型別與實值型別
參考型別:object function
實值型別:number、boolean、string、null、undefined
三、new function(構造器)與prototype(原型)
關於prototype的設計模式就不多說了,網上很多介紹,以一個例子介紹一下js中使用new構造對象的過程。
function classname(){this.id=0;} var v=new classname();
當使用function構造對象時,進行以下流程:
1、 尋找classname的prototype,並進行淺拷貝。
2、 綁定this指標到拷貝來的對象。
3、 將this.constructor屬性設定為classname。
[註:其實classname.prototype.constructor的值也被設定為classname,第六部分會說明]
4、 執行使用者{}中的代碼。
5、 返回this指標賦予左值v。
四、實現物件導向的三個基本特徵
1、 封裝
封裝這個大家都明白,在js中,重點在於存取權限。在其他原生支援物件導向語言中,一般支援public、protected、private三個關鍵字來控制存取權限,但在js中,我們只能依靠複雜的範圍關係來控制: 複製代碼 代碼如下:function classname(a){
var uid=a; //uin為類比private,範圍為{},外部無法使用
this.getuid=function(){return a;} //為uid提供一個外部唯讀介面 obj.getuid();
this.setuid=function(val){a=val} //為uid提供一個外部可寫介面obj.setuid(5);
this.id=uid; //id為類比public obj.id 使用
}
classname.prototype.func=function(){}; //類比public方法 obj.func()調用
classname.stafunc=function(){}; //類比靜態方法 classname.stafunc()調用
var obj=new classname(1);
[!]非常需要注意的就是,因為function是參考型別, classname.prototype.func是所有對象共用的一個function對象(每個對象僅存著引用),因此對象規模不大。而使用this.getuid和this.setuid為定義一個function,因此每個對象執行個體都會存一份,如果放肆使用這種方法,會造成對象規模龐大,影響效能。個人認為類比private變數的意義不大。
[!]如果有需求真的需要大量使用this.xxx=function(){}這種情況,在function(){}中的this指標與最外的this指標是不同的,最好在類定義的首行加上var _this=this;,這樣在this.xxx=function(){}中也可以方便使用綁定的指標。
2、 繼承
繼承的實現,主要有2種方法:第一種是使用javascript本身的原型模型,通過給prototype賦值並改變其constructor屬性來實現繼承;第二種方法是不使用prototype,手動實現將父物件的所有屬性方法深拷貝到子物件。比如A需要繼承B,第一種寫法可以:A.prototype=new B();A.prototype.constructor=A; 第二種寫法可以寫一個遞迴,或者使用jquery中的方法extend。另外,如果要實現多繼承的話,prototype就真的好麻煩了(需要依次多個類,還要建Null 物件來接),第二種方法就比較簡單,依次拷貝即可。一般這種繼承為了找父類方便,可以在對象中加個屬性,引用父類。
3、 多態
函數重載就不說了,都會,檢查參數即可,很靈活。隱藏屬性就是直接賦值undefined。需要注意的是,如果是打算繼承B類的prototype,一定要建一個Null 物件來接,否則的話,你給類寫方法的話,相當於直接修改了prototype,就算不寫方法,你最後修改constructor時也會造成繼承鏈錯亂,接個Null 物件很容易: 複製代碼 代碼如下:function temp(){};
temp.prototype=B;
var obj=new temp();
這樣再讓需要繼承B.prototype的類繼承obj即可,即便修改prototype也不會影響到B。而且也不像繼承new B()那樣浪費很多空間。
五、深拷貝與淺拷貝
這個和其他語言中沒什麼區別,淺拷貝就是直接拷貝,遇到參考型別或類類型不再深入。深拷貝則是根據類型判斷,進行遞迴拷貝。
六、prototype.constructor
這個值主要是用於維護繼承的原型鏈。一篇文章已經寫的非常詳細,請參考:http://bbs.51js.com/thread-84148-1-1.html
七、JS的物件導向開發
由於我不是前台開發人員,見過項目有限,僅談自己的經驗。
我開發過的B/S,常用兩種架構,一種是以CGI為主,由後台語言去產生HTML,JS僅僅做一些使用者互動,ajax通訊等。另外一種是使用MVC,後台語言僅僅產生JSON,View層完全由JS組件在用戶端實現。後者一般大量使用物件導向的思想進行編程,將組件封裝成類,將JSON傳入建構函式,再由控制器或布局組件Add進來。由於組件可以重用,在開發後台管理系統、JS遊戲上,效率還是很可觀的。