1. Why
  2. What
  3. How
  4. API
  5. Source
  6. Remark 

 

Why

JavaScript為原型導向 (prototype-based) 語言

透過 prototype物件的指派,prototype所有的屬性

將成為所有 class (function) instance 的固有屬性

然而這種繼承特性有下列問題

  1. 多個 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
    
    寫過 C++的人,喔不,真正設計過程式的人多少也都知道
    網狀結構絕對會造成開發者混亂的,最簡潔的結構當屬樹狀結構
    也就是單一繼承,因此這狀況應當極力避免
    並且,若 B 的 prototype需要變動,也會影響到 A
    若 B 真的要繼承 A 的話應該如何?
    一般會使用這種作法
    function A(){}
    function B(){}
    
    B.prototype = new A;
    
    A 的 instance 與 A 的 prototype 是兩個不同的物件
    A 的 instance 又屬於 A,因而連帶的又使 B 的 instance 屬於 A
    B 也得以繼承 A 所有成員,並且做其他的修改指派
    這種繼承方式卻很容易被疏忽而造成錯誤

  2. 因為這樣的特性,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
    
  3. 物件導向語言有 method 覆寫 (overwrite) 的特徵
    JavaScript自然也有,但是它卻無從存取原先被覆寫的 method
    除非先把原先的 method 在變更前儲存下來,在新 method內使用它
    但是每個 method 都要這樣做的話可不得了

  4. 「靜態」屬性沒辦法繼承,嚴格說起來 JavaScript (1.0) 沒所謂的靜態屬性
    因為從所有的東西都是物件的觀點來看,class 的觀念也沒那麼嚴謹了
    本處指 class (function) 這個物件本身所持有的屬性

What

jquery-object.js 要達成以下目的

  1. 以自動化的規則完成問題 1 可能造成的疏漏

  2. 由於沒辦法在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模組一種很常用的寫法

  3. 本模組將自動完成所有 method 的繼承
    並提供繼承模組存取基底 method 之方法 (僅限 public 與 protected)

  4. 提供 namespace 概念,使開發人員可建立名稱相同但不同用途的 class
    以及 class 的分門別類

  5. 提供 namespace 別名與 class引入(import) 機制
    使 class 的取得可更為簡化

  6. 擴充原生 Object,增加較為便利的功能與包裝 

  7. 建立根基底 class (名稱 Object)。並建議所有新建 class應繼承之

How

現有 jquery-ui 套件上,已有建立一套類似的繼承機制

jquery-ui 所提供的機制對於 jquery-object 所要達成的目的稍有不足

  1. jquery-ui 並不對靜態成員做其他的繼承

  2. jquery-ui instance 的建立與 method 存取乃是透過特定的 proxy API完成
    且 jquery-ui 對於物件的使用乃以 jquery物件為主
    達成 UI呈現操作的目的,使得它沒辦法應用至其他用途的使用
    若規劃設計不佳,會讓整個程式仍像在撰寫程序導向 (procedure-oriented) 的程式
    但 jquery-object 希望能盡量使 proxy 能夠盡量像在實際一個真正的 class 一般
    營造以物件為主角的思考方式

  3. jquery-ui 的所提供的 namespace 層數僅最多一層

但大抵上主幹是有了,因此 jquery-object 乃以 jquery-ui 的繼承機制為基底改寫而成

並且搭配 jquery 這個跨平台套件庫建置而成

 

API

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

 

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
        成員變數名稱

 

原生Object擴充成員 (IE >= 9)

本段程式碼將會移動至較適當的模組 (參照備註 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成員之值

回傳

    傾印結果之字串

 

根基底 class (Object) 成員

.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全名>]

Source

jquery-object 係由 jquery-ui 增修而成,jquery-ui 為開源程式碼

基於此因,特公開程式碼以供參考

點擊連結觀看

 

Remark

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 將移動至功能擴增完成的原生物件擴充模組上 (近期將撰文)

創作者介紹
創作者 wylokgo101 的頭像
wylokgo101

豆棚瓜架雨如絲 - WYLOKGO101

wylokgo101 發表在 痞客邦 留言(0) 人氣()