javascript面向对象基础 -- 类的创建、继承和扩展:小改prototype的class.create...
时间:2010-08-18 来源:0xff
因项目需要,重新捡起放下两年多的js,再看看Prototype、jQuery,感觉比以前好多了,至少代码看起来越来越顺眼~
不过动起手来还是感觉有不少碍眼的地方。。。
为了效率(既然大家都这么说。。)不得不放弃臃肿的Prototype,而改用VS集成的jQuery,但上手就感到很不适。。
没办法,jQuery里没有很好的面向对象继承机制,只好从Prototype把Class.create方法移植过来。
Class.create有了之后却发现对$super看的很不顺眼(不用Java的结果),于是就顺手把Class.create再改改,就成了这样子。
基类方法的调用都可以通过 this.base.方法名(参数) 原封不动的实现,这下看起来顺眼多了。。
下面是说明和完整代码:
(由prototype的Class.create方法上修改)
使用方法:
定义一个新类:Class.create({类的原型方法列表}, {原型方法列表1}...);
从已有类继承:Class.create({父类}, {子类新增或重写的原型方法列表}, {原型方法列表1}...);
必要函数说明:
定义一个全新的类时,必须实现 initialize 方法
继承说明:
继承后,子类的同名方法将覆盖父类方法,如果需要使用父类方法,可以在子类方法中可以通过 “this.base.父类的方法名称({参数})” 来调用
注意,如果在类定义时创建属性,则所有 基类/子类 的对象都会继承这些属性的引用副本。
例如:var A = Class.create({ P1: 0, P2: [] }); var obj1 = new A(); var obj2 = new A();
obj1.P1++; // 此时 obj1.P1 == 1, obj2.P1 == 0
obj1.P2.push('a'); // 此时 obj1.P1 == obj2.P1 == ['a']
obj1.P2 = ['b']; // 此时 obj1.P1 == ['b'], obj2.P1 == ['a']
关于基类方法交叉调用的层次问题:
在某个基类方法递归链中,如果其中某一级方法调用了其他的基类方法,则此处调用的基类方法为调用方法同级的基类方法。
(即子类继承不会破坏父类原有的基类方法调用链)
如:(以下所示为 类ClassE 的 Method1 从上往下 调用基类的 Method1 方法过程)
ClassE.Method1 ClassE.Method2
ClassD.Method1 ClassD.Method2
ClassC.Method1 ==> this.base.Method2() 调用,此处 Method2 的调用则从当前级别的基类(ClassB)开始。
ClassB.Method1 ClassB.Method2
ClassA.Method1 ClassA.Method2
BaseClass.Method1 BaseClass.Method2
(不推荐在基类方法里出现交叉调用,因为当子类调用基类方法时,基类方法中调用的其他方法可能已被重写,并调用了当前方法,导致出现死循环)
使用示例:
var ClassA = $.fn.Class.create({ initialize: function () { this.name = 'name:ClassA'; return this; }, showMe: function () { alert('ClassA'); return this; }, showName: function () { alert(this.name); return this; } });
var ClassB = $.fn.Class.create(ClassA, { initialize: function () { this.name = 'name:ClassB'; return this; } });
var ClassC = $.fn.Class.create(ClassA, { showMe: function () { alert('ClassC'); return this.base.showMe(); } });
var ClassD = $.fn.Class.create(ClassC, { showMe: function () { alert('ClassD'); return this.base.showMe(); } });
var objA = new ClassA();
var objB = new ClassB();
var objC = new ClassC();
var objD = new ClassD();
objA.showMe().showName(); // 输出:“ClassA”、“name:ClassA”
objB.showMe().showName(); // 输出:“ClassA”、“name:ClassB”
objC.showMe().showName(); // 输出:“ClassC”“ClassA”、“name:ClassA”
objD.showMe().showName(); // 输出:“ClassD”“ClassC”“ClassA”、“name:ClassA”
以下是完整代码(jQuery):
(function ($) {
var Class = (function () {
var IS_DONTENUM_BUGGY = (function () {
for (var p in { toString: 1 }) {
if (p === 'toString') return false;
}
return true;
})();
function subclass() { };
function create() {
/// <summary>
/// 1: Class.create(context) - 创建一个新类
/// 2: Class.create(parenClass, context) - 从父类继承并创建一个新类
/// </summary>
var parent = null, properties = $(arguments).toArray();
if (isFunction(properties[0]))
parent = properties.shift();
function klass() {
/// <summary>
/// [类]请使用 new 新建一个当前类的对象
/// </summary>
/// <param name="plist" type="Object" optional="true">
/// (可选)参数
/// </param>
// 构造对象时,先封装所有基类方法
var self = this;
this.base = { curtor: this.constructor };
initBaseMethods(this.constructor);
function initBaseMethods(baseclass) {
if (baseclass) {
var properties = keys(baseclass.prototype);
// 遍历封装当前类的基类方法
for (var property in baseclass.prototype) {
if (isFunction(baseclass.prototype[property])) addBaseMethod(property);
}
}
}
function addBaseMethod(property) {
// 生成一个同名的基类方法调用函数
self.base[property] = function () {
var curtor = self.base.curtor;
try {
if (curtor.baseclass) {
self.base.curtor = curtor.baseclass;
if (curtor.basemethods[property]) {
// 如果有基类方法,直接调用基类方法
return curtor.basemethods[property].apply(self, arguments);
} else {
// 如果当前类没有同名基类方法,继续往下查找
return self.base[property].apply(self, arguments);
}
} else {
// 如果没有基类,则返回当前类的同名方法
return curtor.prototype[property].apply(self, arguments);
}
} finally {
self.base.curtor = curtor;
}
};
}
// 基类方法封装完毕
// 调用构造函数
if (this.initialize) this.initialize.apply(this, arguments);
}
extend(klass, Class.Methods);
klass.baseclass = parent;
klass.basemethods = {};
klass.subclasses = [];
if (parent) {
subclass.prototype = parent.prototype;
klass.prototype = new subclass;
parent.subclasses.push(klass);
}
for (var i = 0, length = properties.length; i < length; i++)
klass.addMethods(properties[i]);
if (!klass.prototype.initialize)
klass.prototype.initialize = Prototype.emptyFunction;
klass.prototype.constructor = klass;
return klass;
}
function addMethods(source) {
var ancestor = this.superclass && this.superclass.prototype,
properties = keys(source);
if (IS_DONTENUM_BUGGY) {
if (source.toString != Object.prototype.toString)
properties.push("toString");
if (source.valueOf != Object.prototype.valueOf)
properties.push("valueOf");
}
for (var i = 0, length = properties.length; i < length; i++) {
var property = properties[i], value = source[property];
if (isFunction(this.prototype[property])) {
// 发现父类存在同名方法时,将父类方法添加到当前类的基类方法列表
this.basemethods[property] = this.prototype[property];
}
this.prototype[property] = value;
}
return this;
}
return {
create: create,
Methods: {
addMethods: addMethods
}
};
function extend(destination, source) {
for (var property in source)
destination[property] = source[property];
return destination;
}
function getArgumentNames(func) {
var names = func.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
}
function isFunction(obj) {
return Object.prototype.toString.call(obj) === "[object Function]";
}
function keys(object) {
if (typeof (object) !== "object") { throw new TypeError(); }
var results = [];
for (var property in object) {
if (object.hasOwnProperty(property)) {
results.push(property);
}
}
return results;
}
function values(object) {
var results = [];
for (var property in object)
results.push(object[property]);
return results;
}
})();
$.fn.extend({ Class: Class });
})(jQuery);
(注:主体部分不需要框架,可独立使用,把上面的代码去头去尾即可)