MooTools最重要的两个核心模块,一个是Type,另一个就是Class,Type的源码分析已经有棍子上的萝卜分析了1.3版本的了,Class的源码分析网上只有1.2版本的,在1.3版本已经有了大的改变,现在把1.4版的Class尝试分析下,如理解有误欢迎指正:
View Code
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://www.cnblogs.com/hmking) 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 })();