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. })(); 

 

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