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;
})();