Javascript中类创建脚本的优缺点?

时间:2010-10-06 15:51:58

标签: javascript jquery class

我最近编写了一个类创建脚本(在Crockford和Resig的某些部分的代码的帮助下)自动完成某些任务,例如:将默认值与传入的参数合并,需要参数,在DOM和类实例之间创建桥梁,绑定具有公共名称空间的自定义事件,可访问super方法的继承以及mixin的功能。

我的第一个问题是,以这种方式创建课程的利弊是什么?我关注的是性能和可移植性(我的所有类现在都依赖于这个脚本)。

我的第二个问题是,编写类创建脚本的更好方法是什么?代码如下。

(function($){
// Crockford's way of creating new objects
function F(){};
function _createObject(o) {
    F.prototype = o;
    return new F();
};

$.Class = function(){
    var
        prototype = {}
        ,_super = {}
        ,requiredError = ''
        ,extendedInits = []
        ,fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

    // copy properties/methods to prototype
    for (var i = 0, ln = arguments.length; i < ln; ++i){
        var obj = arguments[i];

        // if extending another class
        if ($.isFunction(obj)) _super = obj = obj.prototype;

        if (typeof obj == 'object'){
            for (var prop in obj){
                var objMethod = obj[prop];

                // Resig's Simple Javascript Inheritance
                // if method already exists, map old method to this._super()
                prototype[prop] = typeof objMethod == "function" && typeof _super[prop] == "function" && fnTest.test(obj[prop]) ?
                    (function(prop, fn){
                      return function() {
                        var tmp = this._super;

                        // Add a new ._super() method that is the same method
                        // but on the super-class
                        this._super = _super[prop];

                        // The method only need to be bound temporarily, so we
                        // remove it when we're done executing
                        var ret = fn.apply(this, arguments);
                        this._super = tmp;

                        return ret;
                      };
                    })(prop, objMethod) :
                    // or overwrite the method/property
                    objMethod;

                // if __init method is defined on any of the objects passed in, they will be automatically run at instantiation
                if (prop == '__init') extendedInits.push(obj[prop]);
            }
        }
    }

    prototype.__initialize = function(){
        var
            self = this
            ,dataNS = this.__dataNS
            ,requiredParams = this.__requiredParams;

        // check required parameters
        if (requiredParams){
            for (var i = 0, ln = requiredParams.length; i < ln; ++i){
                var param = requiredParams[i];
                if (arguments.length == 0 || !arguments[0][param]) requiredError = requiredError + param + ', ';
            }
        }

        // if all required params are passed in
        if (requiredError.length == 0){
            // set defaults
            this.cfg = $.extend(true, {}, this.__defaults, arguments[0]);
            this.$elem = this.cfg.$elem;

            // create bridge between dom and instance
            if (dataNS) this.$elem.data(dataNS, this);

            // init class instance
            if (this.init) this.init.apply(this, arguments);

            // init objects instance was extended with
            if (extendedInits.length > 0){
                $.each(extendedInits, function(k, fn){
                    fn.apply(self, arguments);
                });
            }

            // init instance level 
            if (this.cfg.__init) this.cfg.__init.apply(this, arguments);
        }
        else {
            // alert missing properties
            requiredError = requiredError.substring(0, requiredError.lastIndexOf(','));
            var str = 'Required Parameters: ' + requiredError;
            if (dataNS) str = dataNS + ' - ' + str;
            alert(str);
        }
    };

    function _Class(){
        this.__initialize.apply(this, arguments);
    }

    _Class.prototype = _createObject(prototype);
    _Class.constructor = _Class;

    return _Class;
}
})(jQuery);

这就是它的用法:

$.Accordion = new $.Class({
__requiredParams: ['$elem']
,__defaults: {
    speed: 200,
    onlyOneOpen: true,
    classNames: {
        panel: 'panel', panelHeader: 'panelHeader', panelContent: 'panelContent'
    }
}
,panels: {}
,init: function(opts){
    console.log('1st init');
    var self = this;

    // loop thru panels
    this.$elem.find('.' + this.cfg.classNames.panel).each(function(){
        var $this = $(this);
        var panelName = $this.attr('id');
        // uses panel dom element id as name
        self.panels[panelName] = new $.Panel({$elem: $this});
    });

    // panel header on click
    this.$elem.find('.' + this.cfg.classNames.panelHeader).click(function(e){
        e.preventDefault();
        var panelName = $(this).parents('.' + self.cfg.classNames.panel).attr('id');
        var action = (self.panels[panelName].isOpen()) ? 'close' : 'open';
        self[action](panelName);
    });
}
,closeAll: function(){
    $.dispatch('close.panel.accordion');
}
,openAll: function(){
    $.dispatch('open.panel.accordion');
}
,open: function(panelName){
    if (this.cfg.onlyOneOpen) this.closeAll();
    if (typeof this.panels[panelName] != 'undefined') $.dispatch('open.' + panelName + '.accordion');
}
,close: function(panelName){
    if (typeof this.panels[panelName] != 'undefined') $.dispatch('close.' + panelName + '.accordion');
}
}, $.ClassUtils);

注意:上面示例中的$ .ClassUtils是一组混合到$ .Accordion类中的公共方法

1 个答案:

答案 0 :(得分:0)

许多框架都会做你所做的。我对此的主要关注是它试图使javascript不是。 Javascript是原型继承语言。它不是一种经典的继承语言。

这并不是说“班级管理”库没有用,因为它们是有用的。对您的一些反馈:您编写的代码的其他示例使创建类更容易。如果要执行此操作,创建类时的最终结果应该类似于

var myObj =  MyStuff.create({
   prop: prop,
   doSomething: function(){}
 });

就你所用的系统而言,它比你所使用的系统更容易阅读,而且输入更少...