首先在js中如此创建一个类:
var MyClass = function () { /* ... */ }; MyClass.prototype.method = function() { /* ... */ };
要创建一个继承Base的子类:
var temp = function () {}; temp.prototype = Base.prototype; var MyClass = function () { /* ... */ }; MyClass.prototype = new temp(); MyClass.prototype.method = /* ... */;
不能直接写MyClass.prototype = Base.prototype是因为后面给prototype添加method时会同时改变Base的prototype, 所以要用Base的一个实例当作MyClass的prototype.
这个实例有个隐藏的__proto__属性指向了Base的prototype, 所有直接在实例里找不到的属性都会从__proto__里找, 于是这个实例就包含了Base的全部属性成员和方法. 对这个当作MyClass的prototype的实例添加新属性也不会影响到Base的prototype, 并且能够正常的override.
一开始还创建一个空的temp构造函数是为了避免直接调用Base的构筑函数可能产生的副作用. new temp()会返回一个__proto__指向Base的prototype的实例, 但又不会执行Base构造函数.
每创建一个类都这样写真是太丑太麻烦了, 所以需要一个类包装器来方便的创建类.
首先可以写一个函数来完成new temp()的部分:
P.create = function (func) { var r; if (typeof func !== ‘function‘) { func = func.constructor; } r = function () { }; r.prototype = func.prototype; return new r; };
P是个人用的namespace. 如果输入的参数不是一个构筑函数而是一个普通的object, 那么会先取这个object的构造函数.
下面会用到一个浅拷贝的用来混合两个object的函数:
var _oEmpty = {}; P.mix = function (oTarget, oMix) { for (var i in oMix) { if (!(i in _oEmpty) || _oEmpty[i] !== oMix[i]) { oTarget[i] = oMix[i]; } } return oTarget; };
mix会将oMix对象中的所有属性都拷贝到oTarget对象中. 不过, 拷贝前会先将for-in取得的item与一个空object比对, 当且仅当item和空object中的元素不相同时才拷贝到oTarget中. 这样如果oMix修改了默认的toString等属性也可以拷贝到oTarget中, 反之则不会.
需要浅拷贝一个对象也可以用这个函数完成: newObj = P.mix({}, oldObj);
现在可以用mix来扩展MyClass的prototype了:
extend = function (func, obj) { var p = func.prototype; P.mix(p, obj); p.constructor = func; return func; };
mix之后重新把func.prototype.constructor赋值为func, 避免指向obj的constructor.
类包装函数如下:
create = function (fBase, fCons, oProto, oStatic) { var i, l; fBase = fBase || [Object]; if (P.typeOf(fBase) !== ‘array‘) { fBase = [fBase]; } fCons = fCons || function () { }; fCons.superclass = fBase[0]; fCons.prototype = P.create(fBase[0]); for (i = 1, l = fBase.length; i < l; ++i) { extend(fCons, fBase[i].prototype); } oProto && extend(fCons, oProto); oStatic && P.mix(fCons, oStatic); return fCons; };
共有4个参数:
fBase是基类的构造函数, 或者是一个多基类的数组[base, otherBase, ...], 如果输入null等就以Object为基类. 多个基类时, 用排在后面的类的属性覆盖前面的, 但以第一个作为superclass
可选参数fCons是要创建的类的构筑函数, 如果为空就用一个空函数代替
可选参数oProto是成员列表object
可选参数oStatic是静态成员列表object, 即MyClass.xxx
完整的代码:
(function (P) { ‘use strict‘; var C = function () { return C.create.apply(C, arguments); }; C.extend = function (func, obj) { var p = func.prototype; P.mix(p, obj); p.constructor = func; return func; }; C.create = function (fBase, fCons, oProto, oStatic) { var i, l; fBase = fBase || [Object]; if (P.typeOf(fBase) !== ‘array‘) { fBase = [fBase]; } fCons = fCons || function () { }; fCons.superclass = fBase[0]; fCons.prototype = P.create(fBase[0]); for (i = 1, l = fBase.length; i < l; ++i) { C.extend(fCons, fBase[i].prototype); } oProto && C.extend(fCons, oProto); oStatic && P.mix(fCons, oStatic); return fCons; }; return P.Class = C; })(pngx);
一个用这个类包装器创建类的例子:
(function (P, undefined) { ‘use strict‘; P.require(‘class‘); P.require(‘3d/vector3d‘); var M; M = P.Class( Array, function () { var i; this.length = 16; if (arguments.length === 0) { for (i = 16; i--;) { this[i] = (i % 5 === 0) - 0; } } else { for (i = 16; i--;) { this[i] = (arguments[i] - 0) || 0; } } }, { clone: function () { var ret = new M; for (var i = 16; i--;) { ret[i] = this[i]; } return ret; }, append: function (oM3D) { var ret = new M; for (var i = 16, x, y; i--;) { x = (i / 4) | 0; y = i % 4; ret[i] = 0; for (var j = 4; j--;) { ret[i] += this[4 * x + j] * oM3D[4 * j + y]; } } return ret; }, appendScale: function (nSx, nSy, nSz) { return new M( this[0] * nSx, this[1] * nSx, this[2] * nSx, this[3] * nSx, this[4] * nSy, this[5] * nSy, this[6] * nSy, this[7] * nSy, this[8] * nSz, this[9] * nSz, this[10] * nSz, this[11] * nSz, this[12], this[13], this[14], this[15] ); }, appendRotateX: function (nRad) { var s = Math.sin(nRad), c = Math.cos(nRad); return new M( this[0], this[1], this[2], this[3], this[4] * c - this[8] * s, this[5] * c - this[9] * s, this[6] * c - this[10] * s, this[7] * c - this[11] * s, this[4] * s + this[8] * c, this[5] * s + this[9] * c, this[6] * s + this[10] * c, this[7] * s + this[11] * c, this[12], this[13], this[14], this[15] ); }, appendRotateY: function (nRad) { var s = Math.sin(nRad), c = Math.cos(nRad); return new M( this[0] * c + this[8] * s, this[1] * c + this[9] * s, this[2] * c + this[10] * s, this[3] * c + this[11] * s, this[4], this[5], this[6], this[7], this[8] * c - this[0] * s, this[9] * c - this[1] * s, this[10] * c - this[2] * s, this[11] * c - this[3] * s, this[12], this[13], this[14], this[15] ); }, appendRotateZ: function (nRad) { var s = Math.sin(nRad), c = Math.cos(nRad); return new M( this[0] * c - this[4] * s, this[1] * c - this[5] * s, this[2] * c - this[6] * s, this[3] * c - this[7] * s, this[0] * s + this[4] * c, this[1] * s + this[5] * c, this[2] * s + this[6] * c, this[3] * s + this[7] * c, this[8], this[9], this[10], this[11], this[12], this[13], this[14], this[15] ); }, appendTranslate: function (nX, nY, nZ) { return new M( this[0] + nX * this[12], this[1] + nX * this[13], this[2] + nX * this[14], this[3] + nX * this[15], this[4] + nY * this[12], this[5] + nY * this[13], this[6] + nY * this[14], this[7] + nY * this[15], this[8] + nZ * this[12], this[9] + nZ * this[13], this[10] + nZ * this[14], this[11] + nZ * this[15], this[12], this[13], this[14], this[15] ); }, transformVector: function (oV3D) { return new P.Vector3D( this[0] * oV3D.x + this[1] * oV3D.y + this[2] * oV3D.z + this[3], this[4] * oV3D.x + this[5] * oV3D.y + this[6] * oV3D.z + this[7], this[8] * oV3D.x + this[9] * oV3D.y + this[10] * oV3D.z + this[11] ); }, transformVectors: function (aV3D) { var ret = []; for (var i = aV3D.length; i--;) { ret[i] = this.transformVector(aV3D[i]); } return ret; }, det: function () { return this[0] * this[5] * this[10] * this[15] - this[0] * this[5] * this[11] * this[14] - this[0] * this[9] * this[6] * this[15] + this[0] * this[9] * this[7] * this[14] + this[0] * this[13] * this[6] * this[11] - this[0] * this[13] * this[7] * this[10] - this[4] * this[1] * this[10] * this[15] + this[4] * this[1] * this[11] * this[14] + this[4] * this[9] * this[2] * this[15] - this[4] * this[9] * this[3] * this[14] - this[4] * this[13] * this[2] * this[11] + this[4] * this[13] * this[3] * this[10] + this[8] * this[1] * this[6] * this[15] - this[8] * this[1] * this[7] * this[14] - this[8] * this[5] * this[2] * this[15] + this[8] * this[5] * this[3] * this[14] + this[8] * this[13] * this[2] * this[7] - this[8] * this[13] * this[3] * this[6] - this[12] * this[1] * this[6] * this[11] + this[12] * this[1] * this[7] * this[10] + this[12] * this[5] * this[2] * this[11] - this[12] * this[5] * this[3] * this[10] - this[12] * this[9] * this[2] * this[7] + this[12] * this[9] * this[3] * this[6]; }, inverse: function () { var det = this.det(); return new M( (this[5] * this[10] * this[15] - this[5] * this[11] * this[14] - this[9] * this[6] * this[15] + this[9] * this[7] * this[14] + this[13] * this[6] * this[11] - this[13] * this[7] * this[10]) / det, (-this[1] * this[10] * this[15] + this[1] * this[11] * this[14] + this[9] * this[2] * this[15] - this[9] * this[3] * this[14] - this[13] * this[2] * this[11] + this[13] * this[3] * this[10]) / det, (this[1] * this[6] * this[15] - this[1] * this[7] * this[14] - this[5] * this[2] * this[15] + this[5] * this[3] * this[14] + this[13] * this[2] * this[7] - this[13] * this[3] * this[6]) / det, (-this[1] * this[6] * this[11] + this[1] * this[7] * this[10] + this[5] * this[2] * this[11] - this[5] * this[3] * this[10] - this[9] * this[2] * this[7] + this[9] * this[3] * this[6]) / det, (-this[4] * this[10] * this[15] + this[4] * this[11] * this[14] + this[8] * this[6] * this[15] - this[8] * this[7] * this[14] - this[12] * this[6] * this[11] + this[12] * this[7] * this[10]) / det, (this[0] * this[10] * this[15] - this[0] * this[11] * this[14] - this[8] * this[2] * this[15] + this[8] * this[3] * this[14] + this[12] * this[2] * this[11] - this[12] * this[3] * this[10]) / det, (-this[0] * this[6] * this[15] + this[0] * this[7] * this[14] + this[4] * this[2] * this[15] - this[4] * this[3] * this[14] - this[12] * this[2] * this[7] + this[12] * this[3] * this[6]) / det, (this[0] * this[6] * this[11] - this[0] * this[7] * this[10] - this[4] * this[2] * this[11] + this[4] * this[3] * this[10] + this[8] * this[2] * this[7] - this[8] * this[3] * this[6]) / det, (this[4] * this[9] * this[15] - this[4] * this[11] * this[13] - this[8] * this[5] * this[15] + this[8] * this[7] * this[13] + this[12] * this[5] * this[11] - this[12] * this[7] * this[9]) / det, (-this[0] * this[9] * this[15] + this[0] * this[11] * this[13] + this[8] * this[1] * this[15] - this[8] * this[3] * this[13] - this[12] * this[1] * this[11] + this[12] * this[3] * this[9]) / det, (this[0] * this[5] * this[15] - this[0] * this[7] * this[13] - this[4] * this[1] * this[15] + this[4] * this[3] * this[13] + this[12] * this[1] * this[7] - this[12] * this[3] * this[5]) / det, (-this[0] * this[5] * this[11] + this[0] * this[7] * this[9] + this[4] * this[1] * this[11] - this[4] * this[3] * this[9] - this[8] * this[1] * this[7] + this[8] * this[3] * this[5]) / det, (-this[4] * this[9] * this[14] + this[4] * this[10] * this[13] + this[8] * this[5] * this[14] - this[8] * this[6] * this[13] - this[12] * this[5] * this[10] + this[12] * this[6] * this[9]) / det, (this[0] * this[9] * this[14] - this[0] * this[10] * this[13] - this[8] * this[1] * this[14] + this[8] * this[2] * this[13] + this[12] * this[1] * this[10] - this[12] * this[2] * this[9]) / det, (-this[0] * this[5] * this[14] + this[0] * this[6] * this[13] + this[4] * this[1] * this[14] - this[4] * this[2] * this[13] - this[12] * this[1] * this[6] + this[12] * this[2] * this[5]) / det, (this[0] * this[5] * this[10] - this[0] * this[6] * this[9] - this[4] * this[1] * this[10] + this[4] * this[2] * this[9] + this[8] * this[1] * this[6] - this[8] * this[2] * this[5]) / det ); } }, { createRotateTo: function (oVec0, oVec1) { var x, y, z, c, s; x = oVec0.y * oVec1.z - oVec0.z * oVec1.y; y = oVec0.z * oVec1.x - oVec0.x * oVec1.z; z = oVec0.x * oVec1.y - oVec0.y * oVec1.x; c = (oVec0.x * oVec1.x + oVec0.z * oVec1.z) / Math.sqrt((oVec0.x * oVec0.x + oVec0.y * oVec0.y + oVec0.z * oVec0.z) * (oVec1.x * oVec1.x + oVec1.y * oVec1.y + oVec1.z * oVec1.z)); s = Math.sqrt(1 - c * c); return new M( c + (1 - c) * x * x, (1 - c) * x * y - s * z, (1 - c) * x * z + s * y, 0, (1 - c) * y * z + s * z, c + (1 - c) * y * y, (1 - c) * y * z - s * x, 0, (1 - c) * x * z - s * y, (1 - c) * y * z + s * x, c + (1 - c) * z * z, 0, 0, 0, 0, 1 ); } } ); return P.Matrix3D = M; })(pngx);