我最近编写了一个类创建脚本(在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类中的公共方法
答案 0 :(得分:0)
许多框架都会做你所做的。我对此的主要关注是它试图使javascript不是。 Javascript是原型继承语言。它不是一种经典的继承语言。
这并不是说“班级管理”库没有用,因为它们是有用的。对您的一些反馈:您编写的代码的其他示例使创建类更容易。如果要执行此操作,创建类时的最终结果应该类似于
var myObj = MyStuff.create({
prop: prop,
doSomething: function(){}
});
就你所用的系统而言,它比你所使用的系统更容易阅读,而且输入更少...