首页 > 代码库 > 一个javascript类包装器

一个javascript类包装器

首先在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);

 

一个javascript类包装器