JavaScript為原型導向 (prototype-based) 語言
透過 prototype物件的指派,prototype所有的屬性
將成為所有 class (function) instance 的固有屬性
然而這種繼承特性有下列問題
- 多個 class 若持有相同的 prototype 物件
任何一個 function所建立的 instance 將都會屬於這些 class
形成很類似多重繼承的狀況。例如
p = {}; function A(){} function B(){} A.prototype = p; B.prototype = p; a = new A; a instanceof A; // true a instanceof B; // true
網狀結構絕對會造成開發者混亂的,最簡潔的結構當屬樹狀結構
也就是單一繼承,因此這狀況應當極力避免
並且,若 B 的 prototype需要變動,也會影響到 A
若 B 真的要繼承 A 的話應該如何?
一般會使用這種作法
function A(){} function B(){} B.prototype = new A;
A 的 instance 又屬於 A,因而連帶的又使 B 的 instance 屬於 A
B 也得以繼承 A 所有成員,並且做其他的修改指派
這種繼承方式卻很容易被疏忽而造成錯誤 - 因為這樣的特性,prototype內的所有屬性完全沒有 private 屬性
如果要使 class 有 private 屬性成員
必須在 class 內宣告「var 變數名稱; 」
如此使得建立 instance 時的 function執行而建立屬於這個 instance的變數空間
但因為變數 scope的關係,宣告在 class 外部的 prototype 物件
就無法使用到 class 內宣告的 private 變數
讓 prototype 在內部宣告並儲存,就無法使 instance持有這個 prototype的特性
而且程式碼也顯得非常奇怪
function A(){ var P = {'x': 1}; A.prototype = A; } a = new A; a.x; // undefined
- 物件導向語言有 method 覆寫 (overwrite) 的特徵
JavaScript自然也有,但是它卻無從存取原先被覆寫的 method
除非先把原先的 method 在變更前儲存下來,在新 method內使用它
但是每個 method 都要這樣做的話可不得了 - 「靜態」屬性沒辦法繼承,嚴格說起來 JavaScript (1.0) 沒所謂的靜態屬性
因為從所有的東西都是物件的觀點來看,class 的觀念也沒那麼嚴謹了
本處指 class (function) 這個物件本身所持有的屬性
jquery-object.js 要達成以下目的
- 以自動化的規則完成問題 1 可能造成的疏漏
- 由於沒辦法在prototype上真正顯現 public、private等成員的存取權限
本模組將為每個 class 建立各自的 proxy class
proxy擁有 class內的「public」method,就像直接存取該 class 一般
非 method 成員透過特殊的 method存取
protected、private成員則會被阻擋
本模組定義,public,為以非底線字元開始之名稱
protected,為以單一底線開頭之名稱
private,為以雙底線開頭之名稱
註1: 因為 class 還須提供其他模組繼承使用以及便於除錯等原因
設計上並不打算將 class 完全隱藏
開發人員是有辦法直接取得實體 class 的
註2: 由於 JavaScript的設計原則 (終究不是典型的物件導向)
沒辦法自動判斷物件現在處於哪一個 class scope
即使是在「基底」class 的 function中也是見到傳入物件現所持有的屬性
同樣基底class之private成員沒辦法被屏蔽
protected 與 private 的分別只求模組開發人員的謹慎
但是可以利用立即函式 (intermmediate function) 先建立
函式名稱空間,在此空間內使用 local變數
這也是現今開發 javascript模組一種很常用的寫法 - 本模組將自動完成所有 method 的繼承
並提供繼承模組存取基底 method 之方法 (僅限 public 與 protected) - 提供 namespace 概念,使開發人員可建立名稱相同但不同用途的 class
以及 class 的分門別類 - 提供 namespace 別名與 class引入(import) 機制
使 class 的取得可更為簡化 - 擴充原生 Object,增加較為便利的功能與包裝
- 建立根基底 class (名稱 Object)。並建議所有新建 class應繼承之
現有 jquery-ui 套件上,已有建立一套類似的繼承機制
jquery-ui 所提供的機制對於 jquery-object 所要達成的目的稍有不足
- jquery-ui 並不對靜態成員做其他的繼承
- jquery-ui instance 的建立與 method 存取乃是透過特定的 proxy API完成
且 jquery-ui 對於物件的使用乃以 jquery物件為主
達成 UI呈現操作的目的,使得它沒辦法應用至其他用途的使用
若規劃設計不佳,會讓整個程式仍像在撰寫程序導向 (procedure-oriented) 的程式
但 jquery-object 希望能盡量使 proxy 能夠盡量像在實際一個真正的 class 一般
營造以物件為主角的思考方式 - jquery-ui 的所提供的 namespace 層數僅最多一層
但大抵上主幹是有了,因此 jquery-object 乃以 jquery-ui 的繼承機制為基底改寫而成
並且搭配 jquery 這個跨平台套件庫建置而成
jQuery.getClass.reset([context])
功能
初始化 namespace 之別名與引入機制所需之資料區
參數
context : Object (optional)
JavaScript 並未提供 file-scope 特性的功能機制
若將 namespace 資料區歸屬於全域物件
會受到其他檔案內容的影響
因此提供開發人員以分別的 context 物件達成 file-scope的目的
若未指定,則使用 jQuery物件
回傳
初始化完成的 context資料
jQuery.getClass.use(namespace, [context])
功能
建立 namespace 之別名
參數
namespace : String
namespace全名
context : Object
別名與引入機制資料區
若未指定則使用 jQuery物件
jQuery.getClass.import(classFullName, [context])
功能
引入指定class或名稱空間所有 class。
後續的程式開發可使用 class名稱取得該 class
而不須輸入包含 class 與 namespace 的全名
若引入的 class包含相同名稱者
以最後引入者為主
參數
classFullName : String
class全名,若 class名稱為「*」
則引入該namespace 下之所有 class (不包含子namespace)
context : Object
別名與引入機制資料區
若未指定則使用 jQuery物件
jQuery.getClass.resolve(name, [context])
功能
取得輸入名稱之 class全名
若輸入名稱已登入在引入列表內,則回傳解析結果
否則解析namespace別名並回傳全名結果
參數
name : String
需要解析的名稱
context : Object
別名與引入機制資料區
若未指定則使用 jQuery物件\
jQuery.getClass.resolve(name, [context])
功能
取得輸入名稱之 class全名
若輸入名稱已登入在引入列表內,則回傳解析結果
否則解析namespace別名並回傳全名結果
參數
name : String
需要解析的名稱
context : Object
別名與引入機制資料區
若未指定則使用 jQuery物件
回傳
解析完成的 class全名。若未解析成功則回傳輸入值
jQuery.getClass.package(name, [context])
功能
指定模組所在的 package (名稱空間) 位置
指定之後將自動引入該 package所有物件
參數
name : String
package名稱
context : Object
別名與引入機制資料區
若未指定則使用 jQuery物件
jQuery.inherit
(classFullName, [baseClassFullName], prototype, [staticPrototype])
功能
依照基底 class並且以 prototype與 staticPrototype擴充之
並建立 proxy class
若新建之 class 與基底相同,則取代原有的 class
原 class 之子 class 將改繼承於新 class
由原 class 及其子 class 產生 instance 不受新 class影響
特殊成員
this._super : 代表基底 class 之同名 method
若基底 class無該同名 method 或為 private method
則其值為 undefined
this._superApply : 代表基底 class之同名 method
若基底 class無該同名 method 或為 private method
則其值為 undefined
本method參數為一陣列,代表參數列表
this.constructor.version : String,代表模組版本
需於 prototype內指定,若未指定則沿用基底 class
this.namespace : Array,表 namespace之路徑
this.className : String,代表 class名稱
this.classFullName : String,代表 class 全名
參數
classFullName : String
新建 class之全名
baseClassFullName : String
基底 class之全名。若未指定則以 Object (原生) 為基底
prototype : Object
新建 class 本身之 prototype
staticPrototype : Object
新建 class 之靜態屬性
回傳
新建完成之 proxy class
jQuery.inherit(parameter)
功能
以單一物件傳遞class所需參數,較多參數傳遞版本有較多的彈性。
參數
parameter : Object
包含下列欄位:
context:選擇。別名與引入機制資料區。若未指定則使用 jQuery物件
class:必備。新增之class名稱,若已指定模組所在之package,則名稱將代上package名稱
base:選擇。繼承目標,將根據別名與引入機制解析全名。
若未指定或目標不存在,則將使用 root (原生Object) 物件
prototype:必備。新增 class之 prototype
static:選擇。新增 class之靜態屬性
回傳
新建完成之 proxy class
.getProperty(name)
.prototype.getProperty(name)
功能
取得class指定名稱之成員變數值
參數
name : String
成員變數名稱
回傳
指定名稱之成員變數值。若該成員為 method 或 protected、private屬性
則回傳 undefined
.setProperty(name, value)
.prototype.setProperty(name, value)
功能
設定class指定名稱之成員變數值,若該成員為 method
或者 protected 或 private 屬性,則設定無效
註 : 若設定值為 function,則設定後無法再透過 proxy
存取該成員
參數
name : String
成員變數名稱
.removeProperty(name)
.prototype.setProperty(name)
功能
設定class指定名稱之成員變數值,若該成員為 method
或者 protected 或 private 屬性,則設定無效
註 : 若設定值為 function,則設定後無法再透過 proxy
存取該成員
參數
name : String
成員變數名稱
本段程式碼將會移動至較適當的模組 (參照備註 5)
Object.keys()
本函式僅為補足部分瀏覽器版本不支援之問題
若瀏覽器本身支援則不覆寫
Object.copy(object)
功能
將傳入之物件所有屬性,複製之新 Object instance中
註1 : 本函式僅將「可列舉」之成員複製
註2 : 本函式不支援深複製 (deep copy)
參數
object : Object
欲複製之物件
回傳
含有複製物件所有屬性之 Object instance (PlainObject)
Object.createPlainObject([pair1, [pair2, [...]]])
功能
依照傳入之 key-value pair 建立新 Object instance (PlainObject)
參數
... : Array
成員列表,每一參數代表一個成員的 key-value pair
索引 0 代表 key 值,索引 1 則代表 value 值
回傳
新建立之 Object instance (PlainObject)
Object.prototype.propertyCount()
功能
計算物件之成員數量 (僅包含可列舉之成員)。
回傳
物件成員之數量
Object.prototype.keyExists(name)
功能
確定指定名稱之成員是否存在於物件中
參數
name : String
成員變數名稱
回傳
若存在則回傳 true,否則回傳 false
Object.prototype.getValueByOffset(offset)
功能
取得物件中指定序數之成員數值
註 : 成員之序列方式可能依瀏覽器不同而異
本函式較建議用於取得單一成員但成員名稱不定之物件
參數
offset : Integer
成員序數。以 0 為起始
回傳
回傳指定序數之成員變數值。若序數不合法則回傳 undefined。
Object.prototype.equals(object, [options])
功能
比較自己本身與傳入物件之所有成員值是否相同
參數
object : Object
用以比較之物件
options : Object (optional)
比較選項。包含下列成員值
class : 若為 true,則比較傳入物件與自己是否屬於相同之 class
recursive : 若為 true,則進一步比較物件類成員是否相同
比較選項比照目前物件
strict : 若為 true,對成員數值進行嚴格比較 (包含 type比較)
註 : 不嚴格比較下,"1" 與 1 將會被視為相同
undefined、false、null 會被視為相同
回傳
若相同則回傳 true。不同則為 false
Object.prototype.dump([options])
功能
傾印物件內所有 (可列舉) 成員及其變數值。
註 : 深傾印未實作。若欲實作則須克服自身參考之問題
(即成員當中變數值為自己的 reference者)
參數
options : Object or Boolean (optional)
傾印選項,包含下列成員
recursive : 若為 true,則進一步傾印object成員之成員值
若本變數為非物件值,則代表 recursive成員之值
回傳
傾印結果之字串
.instanceOf(class)
.prototype.instanceOf(class)
功能
確認是否本 class 屬於指定之 class
參數
class : String or Class
指定之 class。若為 String 則代表 class之全名
回傳
若屬於指定 class則為 true,否則為 false
.hasInstance(instance)
功能
確認傳入之 instance是否屬於本 class
本 method 乃為非經由本模組產生之 class instance
或非繼承自根基底 class之 class instance所設計
參數
instance : Object
用以確認之 class instance
回傳
若傳入之instance屬於本 class則回傳 true,否則為 false
.toString()
覆寫原有 toString(),回傳值為 [jq-object class <class全名>]
.prototype.toString()
覆寫原有 toString(),回傳值為 [jq-object instance <class全名>]
jquery-object 係由 jquery-ui 增修而成,jquery-ui 為開源程式碼
基於此因,特公開程式碼以供參考
1. 此程式版本為1.0開發中版本
2. 本文僅提供初版模組之功能說明與錯誤修正,其他更新追加功能將擇期撰文
3. 2015/06/18更新,改善getClass系列之功能
4. 2015/08/14更新,新增 package機制與繼承之資料表示法 (notation)
5. 2015/08/24更新,模組更名為 jquery-oo-strengthener.js
刪除原生Object擴充,因為功能分類考量
該段 code 將移動至功能擴增完成的原生物件擴充模組上 (近期將撰文)
留言列表