首页 > 代码库 > MooTools 1.4 源码分析 - Class

MooTools 1.4 源码分析 - Class

/*
---

name: Class

description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.

license: MIT-style license.

requires: [Array, String, Function, Number]

provides: Class

...
*/

(function () {

// #region Class

var Class = this.Class = new Type(‘Class‘, function (params) {
// 如果参数是一个函数,当作构造函数处理,自动变为一个对象字面量,例如:
// var Barn = new Class(function(name){ this.fowl = name; });
if (instanceOf(params, Function)) {
params = { initialize: params };
}

// 先新建一个函数作为新建的类的原型
// 然后调用extend函数把Class所有的特性复制给newClass
// 然后params对象implement到newClass类中,这里调用的implement是Class的implement方法
var newClass = function () {
// 复制前先解除关联,为什么要剥离?因为原型继承,包含引用类型的原型属性会被所有实例共享啊......
reset(this);
// 判断是否处于类的设计阶段
if (newClass.$prototyping) { return this; }
this.$caller = null;
// 类的实例运行阶段调用类本身的构造函数,将参数原样传递
var value = http://www.mamicode.com/(this.initialize) ? this.initialize.apply(this, arguments) : this;
this.$caller = this.caller = null;
return value;
} .extend(this).implement(params);

// 将newClass的构造函数设为Class
newClass.$constructor = Class;
// 指定newClass的原型的$constructor,使之可以正确的instanceOf
newClass.prototype.$constructor = newClass;
// this.parent可以访问父类的被覆盖的方法
newClass.prototype.parent = parent;

// 返回了这个被包装过的类
return newClass;
});

// this.parent可以访问父类的被覆盖的方法
var parent = function () {
if (!this.$caller) {
throw new Error(‘The method "parent" cannot be called.‘);
}
// 通过$name属性取得类方法名称
var name = this.$caller.$name,
// 通过$owner属性得到类对象(不是类的实例),调用类的静态属性parent的到父类对象
parent = this.$caller.$owner.parent,
// 取得父类原型中的同名方法
previous = (parent) ? parent.prototype[name] : null;
// 如果父类中不存在同名的方法,它就抛出一个错误
if (!previous) {
throw new Error(‘The method "‘ + name + ‘" has no parent.‘);
}
// 调用父类中的方法
return previous.apply(this, arguments);
};

/**
* 对象的剥离(也就是clone),这里要详细说明一下reset函数的工作原理:
* 首先创建了一个新的空函数F,然后将F的prototype属性设置为作为参数object传入的原型对象,prototype属性就是用来指向原型对象的,通过原型链机制,
* 它提供了到所有继承而来的成员的链接,最后通过new运算符作用于F创建出一个新对象返回。这个新的对象就是一个以给定对象为原型对象的空对象,
* 以下面的例子来解说,先执行reset(b)语句,然后读取b.ref.x的值,这时你得到的是其原型对象的同名属性值,其实是一个返指最初的a.x的链接,
* 而在这之后你写入b.ref.x一个新值,也就是直接为b.ref对象定义了一个新的属性x,这时你再读取b.ref.x就不是指向a.x了
* 如果想详细了解原型式继承可翻阅JavaScript设计模式一书,非常棒的一本书,真的很棒!!!哈哈......
* var a = { x: 1 };
* var b = { y: 2, ref: a };
* log.info(‘b.ref == a : ‘ + (b.ref == a)); //输出true
* log.info(b.y); // 输出2
* log.info(b.ref.x); // 输出1
* reset(b); //解除引用
* log.info(‘b.ref == a : ‘ + (b.ref == a)); //输出false
* log.info(b.y); // 输出2
* log.info(b.ref.x); // 输出1
* b.ref.x = 10;
* log.info(b.ref.x); // 输出10
* log.info(a.x); // 输出1
**/
var reset = function (object) {
for (var key in object) {
var value = http://www.mamicode.com/object[key];
switch (typeOf(value)) {
case ‘object‘:
var F = function () { };
F.prototype = value;
object[key] = reset(new F);
break;

case ‘array‘:
object[key] = value.clone();
break;
}
}
return object;
};

/**
* @function: wrap
* @description: 将一个方法用wrapper函数重新包装,添加下面几个静态属性
* @$owner - 类本身
* @$origin - 指向未被包装的函数
* @$name - 类的方法名称
* @returns: (funciton) 包装过后的函数
**/
var wrap = function (self, key, method) {
// 如果函数已被父类包装过,则调用最初未被包装的函数(函数的原始形态,呵呵)
if (method.$origin) {
method = method.$origin;
}
var wrapper = function () {
// 如果方法设置了$protected属性,说明此方法不希望在类外面被调用,也就是类的实例不能调用类中设置了$protected属性的方法,只可远观而不可亵玩也,呵呵......
// 但是如果一个类继承另一个类,同时在子类中覆盖了父类中设置了$protected属性的方法,在子类的实例中调用此方法就不会弹出错误警告了。
// 当然如果子类的同名方法同样设置了$protected属性,那么子类的实例同样不能调用此方法。
if (method.$protected && this.$caller == null) {
throw new Error(‘The method "‘ + key + ‘" cannot be called.‘);
}
// 缓存类实例的caller和$caller两个属性
var caller = this.caller,
current = this.$caller;
this.caller = current;
// 将类实例的$caller属性指向调用的方法本身,Function的caller属性在Class中的完美模拟,这样parent函数才可以运行啊,呵呵......
this.$caller = wrapper;
// 挂为原型上的方法执行
var result = method.apply(this, arguments);
// 方法执行完毕将类实例caller和$caller两个属性值还原
this.$caller = current;
this.caller = caller;
return result;
} .extend({ $owner: self, $origin: method, $name: key });
return wrapper;
};

// 又见implement函数,第三次了,呵呵,这里是扩展类的方法属性
var implement = function (key, value, retain) {
// 首先检查类的的每一个属性和方法在Class.Mutators对象的是不是有mutator函数的对应的名字在里面。
// 如果找到了,它就调用这个函数并且把键的值传给它做处理。
if (Class.Mutators.hasOwnProperty(key)) {
value = http://www.mamicode.com/Class.Mutators[key].call(this, value);
// 判断mutator函数有没有返回值,如果没有则退出。
if (value =http://www.mamicode.com/= null) { return this; }
}

if (typeOf(value) == ‘function‘) {
// $hidden属性表明此函数无法被其他对象implement
if (value.$hidden) { return this; }
// Implements mutator调用本函数时retain参数设为ture,表明只是合并方法到原型中
// 而在创建类时(前面建立newclass)retain参数没有赋值,执行wrap函数包装方法到原型
this.prototype[key] = (retain) ? value : wrap(this, key, value);
} else {
// 合并属性到原型
Object.merge(this.prototype, key, value);
}

return this;
};

/**
* @functoin: getInstance
* @param klass - (class) 要继承的类
* @description: 得到父类的一个实例
**/
var getInstance = function (klass) {
// 设置标记,说明Class在设计阶段,不会执行构造函数
klass.$prototyping = true;
var proto = new klass;
// 删除标记
delete klass.$prototyping;
return proto;
};

// 暴露implement方法
Class.implement(‘implement‘, implement.overloadSetter());

// #endregion Class

// #region Mutators

/**
* 好了,接下来着重介绍一下Class.Mutators对象:
*
* Mutator是一个可以改变你的类的结构的一个很特殊的函数,它们是产生特别功能和优雅化继承和掺元的的有力工具。
*
* 建立一个Mutatorr有二个部分:mutator的关键字 和mutator的实际函数,关键字既是mutator的名字,
* 也是在构建类时候的keyword。Mootools把mutators 储存在Class.Mutators对象中。
*
* 当你传一个对象给Class构造函数的时候,Mootools检查这个对象的的每一个键在Class.Mutators对象的是不是有
* mutator函数的对应的名字在里面。如果找到了,它就调用这个函数并且把键的值传给它做处理。
*
* Class.Mutators对象包含了两个内建的Mutator: Extends 和 Implements,分别实现原型式继承和多亲继承。
*
* MooTools在Class.Extras模块中提供了三个掺元类Chain、Events、Options,至于作用就不用多说了吧,呵呵。
**/
Class.Mutators = {

// 取得传送给它的class的名字后,直接继承这个class
Extends: function (parent) {
// 静态属性,存储父类对象
this.parent = parent;
// 原型式继承
this.prototype = getInstance(parent);
},

// Implements mutator取得传送给它的class的名字后,把它们的方法和属性添加到新类。
// 利用掺元类实现多亲继承
Implements: function (items) {
Array.from(items).each(function (item) {
var instance = new item;
for (var key in instance) {
implement.call(this, key, instance[key], true);
}
}, this);
}
};

// #endregion

})();

MooTools 1.4 源码分析 - Class