實現自己的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自己內部的子控件。

源碼下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM