пятница, 31 июля 2015 г.

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

// Extend
function extend (child, parent) {
    var key;
    function F () {}
    if (isTypeOf(child, 'Object') && isTypeOf(parent, 'Object')) {
        for (key in parent) {if (parent.hasOwnProperty(key)) {child[key] = parent[key];}}
    } else if (isTypeOf(child, 'Function') && isTypeOf(parent, 'Object')) {
        child.prototype = parent;
        child.prototype.constructor = child;
    } else if (isTypeOf(child, 'Function') && isTypeOf(parent, 'Function')) {
        for (key in parent) {if (parent.hasOwnProperty(key)) {child[key] = parent[key];}}
        F.prototype = parent.prototype;
        child.prototype = new F();
        child.prototype.constructor = child;
    }
}

// Mixin
function mixin (child) {
    var mixins = Array.prototype.slice.call(arguments, 1);
    if (not(isEmpty(mixins))) {
        if (isTypeOf(mixins[0], 'Array')) {mixins = mixins[0];}
        forEachIn(mixins, function(mixinClass){
            forEachIn(mixinClass, function(propertyInClass, propertyName){if (mixinClass.hasOwnProperty(propertyName)) {child[propertyName] = propertyInClass;}});
            forEachIn(mixinClass.prototype, function(propertyInPrototype, propertyName){if (propertyName !== 'constructor') {child.prototype[propertyName] = propertyInPrototype;}});
        });
    }
}

// Class
function ParentClass (initVar) {
    this.initVar = initVar;
    this.defaultVar = 'ParentClass default var inside constructor.';
    this._x = 0;
    document.addEventListener('click', this._eventFunction, false);
}

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

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

// Public
ParentClass.prototype.destroy = function () {document.removeEventListener('click', this._eventFunction, false);};
ParentClass.prototype.publicMethod = function () {return 'ParentClass public method.';};

// Private
ParentClass.prototype._privateMethod = function () {return 'ParentClass private method call ' + this.initVar;};
ParentClass.prototype._eventFunction = function () {alert(1);};

// Getters and Setters
Object.defineProperty(ParentClass.prototype, "x", {
      get: function () {return this._x;}
    , set: function (value) {this._x = value;}
    , enumerable: true
    , configurable: true
});

// Tests
var parentObject = new ParentClass('ParentClass init var');
console.dir(ParentClass);
console.dir(ParentClass.prototype);
console.log(parentObject.constructor.name);
console.log(parentObject.defaultVar);
delete parentObject.defaultVar;
console.log(parentObject.defaultVar);
console.log(parentObject.initVar);
console.log(parentObject.publicMethod());
console.log(parentObject._privateMethod());
console.log(ParentClass.staticMethod());
parentObject.x = 1;
console.log('ParentClass getter x=' + parentObject.x);
setTimeout(function () {console.log('Object destruction.'); parentObject.destroy(); parentObject = null; }, 5000);

console.log('---------------------------------------------');

// Class
function MixinClass() {}

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

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

// Class
function ChildClass (initVar) {
    if (ChildClass.instance) {return ChildClass.instance;} ChildClass.instance = this;
    ParentClass.call(this, initVar);
    this._y;
    var privateVar = 'ChildClass private var.';
    function privateFunction() {return 'ChildClass private function can show ' + privateVar + ' and ' + ParentClass.prototype._privateMethod.call(ChildClass.instance);}
    console.log(privateFunction());
    return ChildClass.instance;
}

// Extend
extend(ChildClass, ParentClass);

// Mixin
mixin(ChildClass, MixinClass);

// Singleton
ChildClass.instance = undefined;

// Override
ChildClass.prototype.destroy = function () {console.log('ChildClass destroyed.');};
ChildClass.prototype.publicMethod = function () {return 'ChildClass public method.';};

// Getters and Setters
Object.defineProperty(ChildClass.prototype, "y", {
      get: function () {return this._y;}
    , set: function (value) {this._y = value;}
    , enumerable: true
    , configurable: true
});

// Tests
var childObject = new ChildClass('ChildClass init var');
var childObject2 = new ChildClass('ChildClass init var 2');
console.dir(ChildClass);
console.dir(ChildClass.prototype);
console.log('Is singleton? ' + (childObject === childObject2));
console.log(childObject.constructor.name);
console.log(childObject.defaultVar);
console.log(childObject.initVar);
console.log(childObject.publicMethod());
console.log(childObject._privateMethod());
console.log(ChildClass.staticMethod());
childObject.x = 2;
console.log('ChildClass getter x=' + childObject.x);
childObject.y = 5;
console.log('ChildClass getter y=' + childObject.y);
console.log(childObject.mixinPublicMethod());
console.log(ChildClass.mixinStaticMethod());
setTimeout(function () {console.log('Object destruction.'); childObject.destroy(); childObject = null;}, 5000);

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

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