MooTools 1.4 源碼分析 - Class 修正版

    MooTools最重要的兩個核心模塊,一個是Type,另一個就是Class,Type的源碼分析已經有棍子上的蘿蔔分析了1.3版本的了,Class的源碼分析網上只有1.2版本的,在1.3版本已經有了大的改變,現在把1.4版的Class嘗試分析下,如理解有誤歡迎指正:

 

  1. /* 
  2. --- 
  3.  
  4. name: Class 
  5.  
  6. description: Contains the Class Function for easily creating, extending, and implementing reusable Classes. 
  7.  
  8. license: MIT-style license. 
  9.  
  10. requires: [Array, String, Function, Number] 
  11.  
  12. provides: Class 
  13.  
  14. 源碼分析: 苦苦的苦瓜(http://hmking.blog.51cto.com) 
  15.  
  16. ... 
  17. */ 
  18.  
  19. (function () { 
  20.  
  21.     // #region Class 
  22.  
  23.     var Class = this.Class = new Type('Class'function (params) { 
  24.         // 如果參數是一個函數,當作構造函數處理,自動變爲一個對象字面量,例如: 
  25.         // var Barn = new Class(function(name){ this.fowl = name; }); 
  26.         if (instanceOf(params, Function)) { 
  27.             params = { initialize: params }; 
  28.         } 
  29.  
  30.         // 先新建一個函數作爲新建的類的原型 
  31.         // 然後調用extend函數把Class所有的特性複製給newClass 
  32.         // 然後params對象implement到newClass類中,這裏調用的implement是Class的implement方法 
  33.         var newClass = function () { 
  34.             // 複製前先解除關聯,爲什麼要剝離?因爲原型繼承,包含引用類型的原型屬性會被所有實例共享啊...... 
  35.             reset(this); 
  36.             // 判斷是否處於類的設計階段 
  37.             if (newClass.$prototyping) { return this; } 
  38.             this.$caller = null
  39.             // 類的實例運行階段調用類本身的構造函數,將參數原樣傳遞 
  40.             var value = (this.initialize) ? this.initialize.apply(this, arguments) : this
  41.             this.$caller = this.caller = null
  42.             return value; 
  43.         } .extend(this).implement(params); 
  44.  
  45.         // 將newClass的構造函數設爲Class 
  46.         newClass.$constructor = Class; 
  47.         // 指定newClass的原型的$constructor,使之可以正確的instanceOf 
  48.         newClass.prototype.$constructor = newClass; 
  49.         // this.parent可以訪問父類的被覆蓋的方法 
  50.         newClass.prototype.parent = parent; 
  51.  
  52.         // 返回了這個被包裝過的類 
  53.         return newClass; 
  54.     }); 
  55.  
  56.     // this.parent可以訪問父類的被覆蓋的方法 
  57.     var parent = function () { 
  58.         if (!this.$caller) { 
  59.             throw new Error('The method "parent" cannot be called.'); 
  60.         } 
  61.         // 通過$name屬性取得類方法名稱 
  62.         var name = this.$caller.$name, 
  63.         // 通過$owner屬性得到類對象(不是類的實例),調用類的靜態屬性parent的到父類對象 
  64.                 parent = this.$caller.$owner.parent, 
  65.         // 取得父類原型中的同名方法 
  66.                 previous = (parent) ? parent.prototype[name] : null
  67.         // 如果父類中不存在同名的方法,它就拋出一個錯誤 
  68.         if (!previous) { 
  69.             throw new Error('The method "' + name + '" has no parent.'); 
  70.         } 
  71.         // 調用父類中的方法 
  72.         return previous.apply(this, arguments); 
  73.     }; 
  74.  
  75.     /** 
  76.     * 對象的剝離(也就是clone),這裏要詳細說明一下reset函數的工作原理: 
  77.     * 首先創建了一個新的空函數F,然後將F的prototype屬性設置爲作爲參數object傳入的原型對象,prototype屬性就是用來指向原型對象的,通過原型鏈機制, 
  78.     * 它提供了到所有繼承而來的成員的鏈接,最後通過new運算符作用於F創建出一個新對象返回。這個新的對象就是一個以給定對象爲原型對象的空對象, 
  79.     * 以下面的例子來解說,先執行reset(b)語句,然後讀取b.ref.x的值,這時你得到的是其原型對象的同名屬性值,其實是一個返指最初的a.x的鏈接, 
  80.     * 而在這之後你寫入b.ref.x一個新值,也就是直接爲b.ref對象定義了一個新的屬性x,這時你再讀取b.ref.x就不是指向a.x了 
  81.     * 如果想詳細瞭解原型式繼承可翻閱JavaScript設計模式一書,非常棒的一本書,真的很棒!!!哈哈...... 
  82.     * var a = { x: 1 }; 
  83.     * var b = { y: 2, ref: a }; 
  84.     * log.info('b.ref == a : ' + (b.ref == a)); //輸出true 
  85.     * log.info(b.y); // 輸出2 
  86.     * log.info(b.ref.x); // 輸出1 
  87.     * reset(b); //解除引用 
  88.     * log.info('b.ref == a : ' + (b.ref == a)); //輸出false 
  89.     * log.info(b.y); // 輸出2 
  90.     * log.info(b.ref.x); // 輸出1 
  91.     * b.ref.x = 10; 
  92.     * log.info(b.ref.x); // 輸出10 
  93.     * log.info(a.x); // 輸出1 
  94.     **/ 
  95.     var reset = function (object) { 
  96.         for (var key in object) { 
  97.             var value = object[key]; 
  98.             switch (typeOf(value)) { 
  99.                 case 'object'
  100.                     var F = function () { }; 
  101.                     F.prototype = value; 
  102.                     object[key] = reset(new F); 
  103.                     break
  104.  
  105.                 case 'array'
  106.                     object[key] = value.clone(); 
  107.                     break
  108.             } 
  109.         } 
  110.         return object; 
  111.     }; 
  112.  
  113.     /** 
  114.     * @function: wrap 
  115.     * @description: 將一個方法用wrapper函數重新包裝,添加下面幾個靜態屬性 
  116.     * @$owner - 類本身 
  117.     * @$origin - 指向未被包裝的函數 
  118.     * @$name - 類的方法名稱 
  119.     * @returns: (funciton) 包裝過後的函數 
  120.     **/ 
  121.     var wrap = function (self, key, method) { 
  122.         // 如果函數已被父類包裝過,則調用最初未被包裝的函數(函數的原始形態,呵呵) 
  123.         if (method.$origin) { 
  124.             method = method.$origin; 
  125.         } 
  126.         var wrapper = function () { 
  127.             // 如果方法設置了$protected屬性,說明此方法不希望在類外面被調用,也就是類的實例不能調用類中設置了$protected屬性的方法,只可遠觀而不可褻玩也,呵呵...... 
  128.             // 但是如果一個類繼承另一個類,同時在子類中覆蓋了父類中設置了$protected屬性的方法,在子類的實例中調用此方法就不會彈出錯誤警告了。 
  129.             // 當然如果子類的同名方法同樣設置了$protected屬性,那麼子類的實例同樣不能調用此方法。 
  130.             if (method.$protected && this.$caller == null) { 
  131.                 throw new Error('The method "' + key + '" cannot be called.'); 
  132.             } 
  133.             // 緩存類實例的caller和$caller兩個屬性 
  134.             var caller = this.caller, 
  135.                     current = this.$caller; 
  136.             this.caller = current; 
  137.             // 將類實例的$caller屬性指向調用的方法本身,Function的caller屬性在Class中的完美模擬,這樣parent函數纔可以運行啊,呵呵...... 
  138.             this.$caller = wrapper; 
  139.             // 掛爲原型上的方法執行 
  140.             var result = method.apply(this, arguments); 
  141.             // 方法執行完畢將類實例caller和$caller兩個屬性值還原 
  142.             this.$caller = current; 
  143.             this.caller = caller; 
  144.             return result; 
  145.         } .extend({ $owner: self, $origin: method, $name: key }); 
  146.         return wrapper; 
  147.     }; 
  148.  
  149.     // 又見implement函數,第三次了,呵呵,這裏是擴展類的方法屬性 
  150.     var implement = function (key, value, retain) { 
  151.         // 首先檢查類的的每一個屬性和方法在Class.Mutators對象的是不是有mutator函數的對應的名字在裏面。 
  152.         // 如果找到了,它就調用這個函數並且把鍵的值傳給它做處理。 
  153.         if (Class.Mutators.hasOwnProperty(key)) { 
  154.             value = Class.Mutators[key].call(this, value); 
  155.             // 判斷mutator函數有沒有返回值,如果沒有則退出。 
  156.             if (value == null) { return this; } 
  157.         } 
  158.  
  159.         if (typeOf(value) == 'function') { 
  160.             // $hidden屬性表明此函數無法被其他對象implement 
  161.             if (value.$hidden) { return this; } 
  162.             // Implements mutator調用本函數時retain參數設爲ture,表明只是合併方法到原型中 
  163.             // 而在創建類時(前面建立newclass)retain參數沒有賦值,執行wrap函數包裝方法到原型 
  164.             this.prototype[key] = (retain) ? value : wrap(this, key, value); 
  165.         } else { 
  166.             // 合併屬性到原型 
  167.             Object.merge(this.prototype, key, value); 
  168.         } 
  169.  
  170.         return this
  171.     }; 
  172.  
  173.     /** 
  174.     * @functoin: getInstance 
  175.     * @param klass - (class) 要繼承的類 
  176.     * @description: 得到父類的一個實例 
  177.     **/ 
  178.     var getInstance = function (klass) { 
  179.         // 設置標記,說明Class在設計階段,不會執行構造函數 
  180.         klass.$prototyping = true
  181.         var proto = new klass; 
  182.         // 刪除標記 
  183.         delete klass.$prototyping; 
  184.         return proto; 
  185.     }; 
  186.  
  187.     // 暴露implement方法 
  188.     Class.implement('implement', implement.overloadSetter()); 
  189.  
  190.     // #endregion Class 
  191.  
  192.     // #region Mutators 
  193.  
  194.     /** 
  195.     * 好了,接下來着重介紹一下Class.Mutators對象: 
  196.     * 
  197.     * Mutator是一個可以改變你的類的結構的一個很特殊的函數,它們是產生特別功能和優雅化繼承和摻元的的有力工具。 
  198.     * 
  199.     * 建立一個Mutatorr有二個部分:mutator的關鍵字 和mutator的實際函數,關鍵字既是mutator的名字, 
  200.     * 也是在構建類時候的keyword。Mootools把mutators 儲存在Class.Mutators對象中。 
  201.     * 
  202.     * 當你傳一個對象給Class構造函數的時候,Mootools檢查這個對象的的每一個鍵在Class.Mutators對象的是不是有 
  203.     * mutator函數的對應的名字在裏面。如果找到了,它就調用這個函數並且把鍵的值傳給它做處理。 
  204.     * 
  205.     * Class.Mutators對象包含了兩個內建的Mutator: Extends 和 Implements,分別實現原型式繼承和多親繼承。 
  206.     * 
  207.     * MooTools在Class.Extras模塊中提供了三個摻元類Chain、Events、Options,至於作用就不用多說了吧,呵呵。 
  208.     **/ 
  209.     Class.Mutators = { 
  210.  
  211.         // 取得傳送給它的class的名字後,直接繼承這個class 
  212.         Extends: function (parent) { 
  213.             // 靜態屬性,存儲父類對象 
  214.             this.parent = parent; 
  215.             // 原型式繼承 
  216.             this.prototype = getInstance(parent); 
  217.         }, 
  218.  
  219.         // Implements mutator取得傳送給它的class的名字後,把它們的方法和屬性添加到新類。 
  220.         // 利用摻元類實現多親繼承 
  221.         Implements: function (items) { 
  222.             Array.from(items).each(function (item) { 
  223.                 var instance = new item; 
  224.                 for (var key in instance) { 
  225.                     implement.call(this, key, instance[key], true); 
  226.                 } 
  227.             }, this); 
  228.         } 
  229.     }; 
  230.  
  231.     // #endregion 
  232.  
  233. })(); 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章