понедельник, 7 сентября 2015 г.

Упрощенный способ создания класса в JavaScript

Примечание: в данном способе возможно содержится небольшая ошибка.

<!DOCTYPE html>
<html>
<head>
<script>

var ParentClass = new Class (function () {

    // constructor
    function ParentClass (initVar) {
        this.initVar = initVar;
        this._x = 0; // getter | setter
        document.addEventListener('click', this._privateMethod, false);
        console.log('ParentClass constructed.');
    }

    // destructor
    ParentClass.prototype.destroy = function () {
        document.removeEventListener('click', this._privateMethod, false);
        console.log('ParentClass destroyed.');
    };

    // default
    ParentClass.prototype.defaultVar = 'ParentClass default var.';

    // public
    ParentClass.prototype.publicMethod = function () {return 'ParentClass public method.';};

    // private - interface check
    ParentClass.prototype._privateMethod = function () {console.log('ParentClass private method.');};

    // abstract - interface check
     ParentClass.prototype.abstractMethod = function () {};

    // final - interface check
    ParentClass.prototype.finalMethod = function () {return 'ParentClass final method.';};

    // static
    ParentClass.staticMethod = function () {return 'ParentClass static method.';};

    // getter | setter
    Object.defineProperty(ParentClass.prototype, 'x', {
          get: function () {return this._x;}
        , set: function (value) {this._x = value;}
        , enumerable: true
        , configurable: true
    });

    return ParentClass;

});

var MixinClass = new Class (function () {

    // constructor
    function MixinClass () {}

    // public
    MixinClass.prototype.mixinPublicMethod = function () {return 'MixinClass public method.';};

    // static
    MixinClass.mixinStaticMethod = function  () {return 'MixinClass static method.';};

    return MixinClass;

});

var ChildClass = new Class ({extends: ParentClass, mixin: MixinClass}, function (_super) {

    // singleton
    var singleton;

    // constructor
    function ChildClass (initVar) {
        if (singleton) {return singleton} else {singleton = this;}
        _super.call(this, initVar);
    }

    // public - parent call
    ChildClass.prototype.childPublicMethod = function () {
        return 'ChildClass childPublicMethod call ' + _super.prototype.publicMethod.call(this);
    };

    // override
    ChildClass.prototype.abstractMethod = function () {return 'ChildClass override ParentClass abstarctMethod.';};

    // override private
    ChildClass.prototype._privateMethod = function () {console.log('ChildClass private method.');};

    return ChildClass;

});

function _mixin (ChildClass) {
    var mixinClasses = Array.prototype.slice.call(arguments, 1)
        , key
        , len
        , i;
    if (mixinClasses.length > 0) {
        if (Object.prototype.toString.call(mixinClasses[0]) === '[object Array]') {mixinClasses = mixinClasses[0];}
        for (i = 0, len = mixinClasses.length; i < len; i++) {
            for (key in mixinClasses[i]) {if (mixinClasses[i].hasOwnProperty(key)) {ChildClass[key] = mixinClasses[i][key];}}
            for (key in mixinClasses[i].prototype) {if (key !== 'constructor') {ChildClass.prototype[key] = mixinClasses[i].prototype[key];}}
        }
    }
}

function Class () {
    var Constructor
        , len
        , key
        , i
        , tempObject;
    function F () {}
    if (
              arguments.length === 1
        && Object.prototype.toString.call(arguments[0]) === '[object Function]'
    ) {
        return arguments[0]();
    } else if (
              arguments.length === 2
        && Object.prototype.toString.call(arguments[0]) === '[object Object]'
        && Object.prototype.toString.call(arguments[1]) === '[object Function]'
    ) {
        if (arguments[0].hasOwnProperty('extends')) {
            Constructor = arguments[1](arguments[0]['extends']);
            for (key in arguments[0]['extends']) {
                if (!Constructor.hasOwnProperty(key)) {Constructor[key] = arguments[0]['extends'][key];}
            }
            F.prototype = arguments[0]['extends'].prototype;
            tempObject = new F();
            for (key in tempObject) {
                if (!Constructor.prototype.hasOwnProperty(key)) {Constructor.prototype[key] = tempObject[key];}
            }
        } else {
            Constructor = arguments[1]();
        }
        if (arguments[0].hasOwnProperty('mixin')) {_mixin(Constructor, arguments[0]['mixin']);}
        return Constructor;
    } else {
        throw new Error('Argument of new Class constructor must be object or function');
    }
}

console.log('ParentClass static');
console.dir(ParentClass);
console.log('ParentClass public');
console.dir(ParentClass.prototype);

var parentObject = new ParentClass('ParentClass init var');
console.log('1) Constructor name: ' + parentObject.constructor.name);
console.log('2) DeafultVar value: ' + parentObject.defaultVar);
console.log('3) InitVar value: ' + parentObject.initVar);
console.log('4) PublicMethod result: ' + parentObject.publicMethod());
console.log('5) AbstractMethod result: ' + parentObject.abstractMethod());
console.log('6) FinalMethod result: ' + parentObject.finalMethod());
console.log('7) StaticMethod result: ' + ParentClass.staticMethod());
console.log('8) Get x result: ' + parentObject.x);
console.log('9) Set x.');
parentObject.x = 1;
console.log('10) Get x after set: ' + parentObject.x);
setTimeout(function(){console.log('11) Object destruction. '); parentObject.destroy(); parentObject = null;}, 5000);

console.log('MixinClass static');
console.dir(MixinClass);
console.log('MixinClass public');
console.dir(MixinClass.prototype);

console.log('ChildClass static');
console.dir(ChildClass);
console.log('ChildClass public');
console.dir(ChildClass.prototype);

var childObject = new ChildClass('ChildClass init var 1');
console.log('1) ChildClass object 1 created.');
var childObject2 = new ChildClass('ChildClass init var 2');
console.log('2) ChildClass object 2 created.');
console.log('3) Is singleton? ' + (childObject === childObject2));
console.log('4) Constructor name: ' + childObject.constructor.name);
console.log('5) DeafultVar value: ' + childObject.defaultVar);
console.log('6) InitVar value: ' + childObject.initVar);
console.log('7) PublicMethod result: ' + childObject.publicMethod());
console.log('8) ChildPublicMethod result: ' + childObject.childPublicMethod());
console.log('9) AbstractMethod result: ' + childObject.abstractMethod());
console.log('10) FinalMethod result: ' + childObject.finalMethod());
console.log('11) StaticMethod result: ' + ChildClass.staticMethod());
console.log('12) Get x result: ' + childObject.x);
console.log('13) Set x.');
childObject.x = 2;
console.log('14) Get x after set: ' + childObject.x);
console.log('15) MixinPublicMethod result: ' + childObject.mixinPublicMethod());
console.log('16) MixinStaticMethod result: ' + ChildClass.mixinStaticMethod());
setTimeout(function(){console.log('17) Object destruction. '); childObject.destroy(); childObject = null;}, 5000);

</script>
</head>
<body>
</body>
</html>

Комментариев нет:

Отправить комментарий