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