js类有很多中写法,网上也有很多现成的库,都有各自的优缺点,但用起来都不太顺手。所以总结改进后,写了自己的类,只图用起来顺手,看起来顺眼。
比较喜欢mootools,语法和它类似。initialize为构造方法,statics为静态属性及方法,superclass为父类;在当前方法中调用父类方法使用this.callSuper(),在当前方法中调用父类的其它方法this.invokeSuper('methodName').execute(args);
js中类的写法有一个痛点,即没法完全实现私有属性方法、受保护属性方法。mootools中有相关的内容,但也只是在继承的层面上实现部分功能,并不能控制一个对象外部对私有方法、受保护方法的访问。
使用方法如下:
Class('myclass.A',{ initialize:function(){ this.name='A'; }, showName:function(){ alert(this.name); } }); Class('myclass.B',{ superclass:myclass.A, initialize:function(){ this.name='B'; }, showSuperName:function(){ this.invokeSuper('showName').execute(); }, showName:function(){ alert('<<<') this.callSuper(); alert('>>>') } }); var a=new myclass.A(); a.showName(); var b=new myclass.B(); b.showName(); var b=new myclass.B(); b.showSuperName();
源码如下,仍需完善,仅供大家参考。
(function () { var noArgs = [], emptyFn = function () { }, noInvokeResult = { execute: emptyFn }, invokeSuperResult = { execute: function () { var result = this.method.apply(this.obj, arguments || noArgs); this.obj = null; this.method = null; return result; } }, enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor']; function namespace() { // namespace var a = arguments, o = null, i, j, d, rt; for (i = 0; i < a.length; ++i) { d = a[i].split("."); rt = d[0]; eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';'); for (j = 1; j < d.length; ++j) { o[d[j]] = o[d[j]] || {}; o = o[d[j]]; } } return o; } function inherit(C, superClass) { if (typeof(superClass) != 'function') { return C; } function F() {} F.prototype = superClass.prototype; C.prototype = new F(); C.prototype.constructor = C; C.superclass = superClass; return C; } function addMethods(C, methods) { var p = C.prototype; for (var name in methods) { var m = methods[name]; m['_name_'] = name; m['_owner_'] = C; p[name] = m; } return C; } //TODO 1、需增加获取静态属性、方法的方法2、增加获取祖先的方法ancestor function callSuper() { var method = arguments.callee.caller, superMethod; if (method) { if (method._owner_) { superMethod = method._owner_.superclass.prototype[method._name_]; } else if (method.superclass) { superMethod = method.superclass; } else { superMethod = emptyFn; } return superMethod.apply(this, arguments || noArgs); } } var staticUtil = { getStatic: function (name) { return this.constructor[name]; } }; var invokeSuper = (function () { var obj, superMethod, proxyResult = { execute: function () { var result = superMethod.apply(obj, arguments || noArgs); obj = null; superMethod = null; return result; } }; function proxy(name) { try { superMethod = proxy.caller._owner_.superclass.prototype[name]; if (!superMethod) { throw(0); } obj = this; return proxyResult; } catch (e) { throw(new Error("[invokeSuper error]: the method " + name + "'s super is not exist!")); } } return proxy; })(); function Class() { var options, initialize, superclass, statics, mixin, fullName, className, path; if (arguments.length == 1) { options = arguments[0]; } else if (arguments.length == 2) { fullName = arguments[0]; path = fullName.split("."); className = path.pop(); if (path.length > 0) { path = namespace(path.join('.')); } else { path = window; } options = arguments[1]; } else { fullName = ""; className = ""; } if ('initialize' in options) { initialize = options['initialize']; delete options['initialize']; } else { initialize = function () { }; } // TODO火狐中name属性无法赋值 if (options.hasOwnProperty('statics')) { statics = options['statics']; for (var k in statics) { if (statics.hasOwnProperty(k)) { initialize[k] = statics[k]; } } delete options['statics']; addMethods(initialize, staticUtil); } if ('superclass' in options) { superclass = options['superclass']; if (superclass) { inherit(initialize, superclass); superclass.prototype.callSuper || addMethods(initialize, { callSuper: callSuper, invokeSuper: invokeSuper }); delete options['superclass']; } else { throw TypeError("the superclass of '" + fullName + "' is undefined!"); } } addMethods(initialize, options); if (options.hasOwnProperty('mixin')) { mixin = options['mixin']; if(mixin.length&&mixin.pop){ for(var i=0;mixin[i]!=undefined;i++){ addMethods(initialize,mixin[i]); } }else{ addMethods(initialize,mixin); } delete options['mixin']; } if (className) { path[className] = initialize; path = arguments[0]; } initialize._isClass_ = true; initialize._name_ = className; initialize._fullName_ = fullName; return initialize; } window.Class = Class; })();