zoukankan      html  css  js  c++  java
  • MooTools 1.4 源码分析

    /*
    ---

    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 = (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 = 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 = Class.Mutators[key].call(this, value);
    // 判断mutator函数有没有返回值,如果没有则退出。
    if (value == 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

    })();

  • 相关阅读:
    什么是MongoDb
    Python之人工智能:PyAudio 实现录音 自动化交互实现问答
    Python人工智能之初识接口
    cordova(安卓)(腾讯信鸽注册绑定与反绑定) 插件开发
    sencha touch 在安卓中横屏、竖屏切换 应用崩溃问题
    Sencha Touch 实战开发培训 电子书 基础篇
    wps 批量调整图片大小 宏
    使用 crosswalk-cordova 打包sencha touch 项目,再也不用担心安卓兼容问题!
    Sencha Cmd 5.0.1.231 是坑爹货
    sencha touch api 使用指南
  • 原文地址:https://www.cnblogs.com/haohaoday/p/3949161.html
Copyright © 2011-2022 走看看