zoukankan      html  css  js  c++  java
  • 实现自己的js框架

    两年前写的,现在发出来,也当是一个记录。

    我知道贴代码有些人会反对,觉得应该用文字讲细致,但是我觉得用文字我没发用简单的文字来表达,看代码反而更直接,这个是见仁见智的。

    很早之前一直用extjs,这个确实非常强大,但是在IE下一直都有一个性能的问题,搞得要让客户换chrome,firefox浏览器才行。

    之所以要写自己js框架,原因是觉得自己实现也简单,维护也方便,一些管理应用的项目可以用,网站这些我还是推荐直接使用jquery,毕竟更加简单直接没有那么多可给你重复应用的地方。

    可能你要问为什么不用jquery插件,一样可以。但是这些插件代码质量不能保证,风格也是奇形怪状,出了问题还要去看代码,改了之后又跟原作者的有出入,以后同步更加麻烦。

    不用jquery插件,但是jquery却是个好东西,解决了浏览器兼容的大问题,所以我这个就是基于jquery来实现的,因为一直喜欢extjs的代码风格,所以如果你使用过extjs的话,使用起来会轻车熟路。

    js的继承是原型继承的方式,最开始的时候我确实也用原型的方式来实现,但是后面我发现有些问题,而且理解起来不是太清晰,所以我采用一种简单清晰的方法,写起来是继承但是实际上它只是一个对象。

    它同样有着抽象方法,有父类,有重载,有重写等等。

    先看一下大概结构如下图:

    现在说下几个重要的地方

    首先就是如何定义一个类

    define: function (className, defaultConfig) {
            if (!wyl.isString(className) || className.isEmpty()) {
                throw new Error("[wyl.define] Invalid class name '" + className + "' specified, must be a non-empty string");
            }
            var namespace_arr = className.split('.');
            var namespace = window;
            var class_Name = namespace_arr.last();
            wyl.each(namespace_arr, function (ns) {
                if (ns == class_Name) { return; }
                if (!namespace[ns]) {
                    namespace[ns] = {};
                }
                namespace = namespace[ns];
            });
            if (namespace[class_Name]) {
                throw new Error('重复定义了' + className);
            }
            defaultConfig.className = className;
            for (var p in defaultConfig) {
                if (wyl.isFunction(defaultConfig[p])) {
                    defaultConfig[p].fnName = p;
                }
            }
            namespace[class_Name] = defaultConfig;
        }
    

    接着就是如何创建一个类的实例:

    getDefineConfig: function (namespaces, i, context) {
            if (context == null) { return null; }
            if (namespaces.length == i) {
                return context;
            }
            var ns = namespaces[i];
            return wyl.getDefineConfig(namespaces, i + 1, context[ns]);
    
        }
    
        create: function (className, config) {
            if (!wyl.isString(className) || className.isEmpty()) {
                throw new Error("[wyl.create] Invalid class name '" + nsclassName + "' specified, must be a non-empty string");
            }
            var namespaces = className.split('.');
            var defineConfig = wyl.getDefineConfig(namespaces, 0, window);
            if (!defineConfig) { throw '{0}类型未定义。'.format(className); }
            config = config || {};
            if (!config.id) { config.id = wyl.newId(); }
            var instance = {};
            wyl.apply(instance, config, defineConfig);
    //继承的关键代码如下
            if (instance.extend) {
                var base = wyl.create(instance.extend);
                for (var p in base) {
                    if (instance.hasOwnProperty(p)) {
    //把父类的方法重新命名赋值到这个实例上
                        if (wyl.isFunction(instance[p]) && wyl.isFunction(base[p])) {
                            var baseFnName = instance.extend + '_' + p;
                            instance[p]['baseFnName'] = instance.extend + '_' + p;
                            instance[baseFnName] = base[p];
                        }
                    }
                    else {
                        instance[p] = base[p];
                    }
                }
            }
            instance.initConfig = config;
            if (typeof (instance.init) == 'function') {
                instance.init.apply(instance);
            }
            if (wyl.isFunction(instance.initComponent)) {
                instance.initComponent.apply(instance);
            }
            wyl.each(instance.abstractMethods, function (methodName) {
                this[methodName] = function () { throw this.className + ' has not implement ' + methodName; }
            }, this);
            this.intanceMgr[instance.id] = instance;
            return instance;
        }
    

    对于所有的类型我定义了一个基类

    wyl.define('wyl.object', {
        initConfig: null,
        abstractMethods: [],
    //ext4.0中调用父类的方法就是这个
        callParent: function () {
            var me = this;
    //调用的时候根据之前create里面加入的隐藏信息baseFnName来找到父类的方法并调用它
            var fnname = me.callParent.caller['baseFnName'];
            var fn = me[fnname];
            if (wyl.isFunction(fn)) {
                fn.call(me);
            }
        },
        addEvents: function () {
            var me = this;
            if (!wyl.eventMgr[me.id]) { wyl.eventMgr[me.id] = {}; }
            var myEventMgr = wyl.eventMgr[me.id];
            wyl.each(arguments, function (eventName) {
                if (!myEventMgr[eventName]) { myEventMgr[eventName] = []; }
            });
        },
        fireEvent: function (eventName, prams) {
            var me = this;
            var myEventMgr = wyl.eventMgr[me.id];
            var listeners = myEventMgr[eventName];
            if (wyl.isArray(listeners)) {
                wyl.each(listeners, function (listener) {
                    var scope = listener.scope ? listener.scope : this;
                    if (listener.callback && typeof (listener.callback) == 'function') {
                        listener.callback.call(scope, prams);
                    }
                });
            }
        },
        on: function (eventName, options) {
            var me = this;
            if (!wyl.eventMgr[me.id]) { wyl.eventMgr[me.id] = {}; }
            var myEventMgr = wyl.eventMgr[me.id];
            if (!myEventMgr[eventName]) { myEventMgr[eventName] = []; }
            var removeIndex = null;
            wyl.each(myEventMgr[eventName], function (item, i) {
                var b = options.callback == item.callback;
                if (b) { removeIndex = i; }
                return !b;
            });
            if (removeIndex && removeIndex > -1) {
                myEventMgr[eventName].removeAt(removeIndex);
            }
            myEventMgr[eventName].push(options);
        },
        destroy: function () {
            delete wyl.eventMgr[this.id];
            delete wyl.intanceMgr[this.id];
        },
        init: function () {
            wyl.apply(this, this.initConfig);
        }
    });
    

    界面的控件,我也定义了一个基类:

    wyl.define('wyl.Component', {
        extend: 'wyl.object',
        containerSelector: null,
        container: null,
        abstractMethods: ['render'],
        getContainer: function () {
            if (this.container === null) {
                this.container = $(this.containerSelector).first();
                if (this.container == null) { throw '控件必须要设置containerSelector或不存在.'; }
            }
            return this.container;
        },
        setContainer: function (selector) {
            if (typeof (selector) != 'string') { throw '设置container必须是string类型.'; }
            this.containerSelector = selector;
        },
        getWidth: function () {
            return this.getContainer().width() - 2;
        },
        getHeight: function () {
            return this.getContainer().height() - 2;
        }
    });
    

    大概的思路就是容器会负责分配给下面子控件的容器setContainer,子控件得到自己的容器getContainer,一个控件不需要关系其他控件,只需要控制好自己内部的控件元素就行了。

    里面定义了一个抽象方法render,每个控件都必须实现这个方法。容器render的时候,同时如果自己内部有子控件,也render自己内部的子控件。

    源码下载

  • 相关阅读:
    成家撑家 不要妄自菲薄
    [文字20091204]佛说贪、嗔、痴、妒、慢、疑
    [文摘20091116]一生必看的88本书
    [文摘201009]演讲录全文:美国世界帝国战略与中国的危机 戴旭
    由 图标 加 文字 实现 按钮功能 的 图标按钮用户控件
    javascript Array扩展
    javascript Number对象
    纯CSS多级菜单2
    纯CSS相册2
    纯CSS多级菜单
  • 原文地址:https://www.cnblogs.com/bmrxntfj/p/wyljs.html
Copyright © 2011-2022 走看看