четверг, 23 июля 2015 г.

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

<!DOCTYPE html>
<html>
<meta charset="utf-8" />
<head><title>JavaScript Class</title></head>
<body>
<script type="text/javascript">

function class_ (cls) {
    if (Object.prototype.toString.call(cls) !== '[object Function]') {throw new Error('Class constructor must be a function.');}
    return cls();
}

function extend_ (child, parent) {
    var key;
    function F () {}
    if (Object.prototype.toString.call(child) === '[object Object]' && Object.prototype.toString.call(parent) === '[object Object]') {
        for (key in parent) {if (parent.hasOwnProperty(key)) {child[key] = parent[key];}}
        return;
    }
    if (Object.prototype.toString.call(child) === '[object Function]' && Object.prototype.toString.call(parent) === '[object Object]') {
        child.prototype = parent;
        child.prototype.constructor = child;
        child.super_ = parent;
    }
    if (Object.prototype.toString.call(child) === '[object Function]' && Object.prototype.toString.call(parent) === '[object 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;
        child.super_ = parent.prototype;
    }
}

function mixin_ (child) {
    var mixins = Array.prototype.slice.call(arguments, 1)
        , i, key, len = mixins.length;
    if (len) {
        for (i = 0; i < len; i++) {
            for (key in mixins[i]) {if (mixins[i].hasOwnProperty(key)) {child[key] = mixins[i][key];}}
            for (key in mixins[i].prototype) {child.prototype[key] = mixins[i].prototype[key];}
        }
    }
}

function singleton_() {return {for_: function (cls) {cls.instance_ = undefined;}}}

function create_singleton_ (cls) {
    if (cls.instance_ !== undefined) {return cls.instance_;} else {return cls.instance_ = new cls();}
}

function constructor_ (func) {return {for_: function (cls) {cls.new_ = func;}}}

function destructor_ (func) {return {for_: function (cls) {cls.prototype.destroy_ = func;}}}

function default_ (propName, propValue) {return {for_: function (cls) {cls.prototype[propName] = propValue;}}}

function public_ (propName, propValue) {return {for_: function (cls) {cls.prototype[propName] = propValue;}}}

function private_ (propName, propValue) {return {for_: function (cls) {
        if (propName.substring(0, 1) !== '_') {throw new Error('Private property name must starts from symbol "_".');}
        cls.prototype[propName] = propValue;
    }}
}

function override_ (propName, propValue) {return {for_: function (cls) {cls.prototype[propName] = propValue;}}}

function final_ (propName, propValue) {return {for_: function (cls) {cls.prototype[propName] = propValue;}}}

function static_ (propName, propValue) {return {for_: function (cls) {cls[propName] = propValue;}}}

function implementation_ (cls, propArray) {
    for (var i = 0, len = propArray.length; i < len; i++) {propArray[i].for_(cls);}
}

function interface_ (intfc) {
    if (Object.prototype.toString.call(intfc) !== '[object Function]') {throw new Error('Interface constructor must be a function.');}
    return intfc();
}

function impliment_(cls, intfc) {
    var key, errorMessage = 'Class does not implement the interface correctly for "';
    for (key in intfc) {if (!(key in cls)) {throw new Error(errorMessage + key + '".');}}
    for (key in intfc.prototype) {if (!(key in cls.prototype)) {throw new Error(errorMessage + key + '".');}}
}

var AbstractClass = class_(function(){
    function AbstractClass () {}
    implementation_(AbstractClass, [
          constructor_(function () {var instance = new AbstractClass(); return instance;})
        , destructor_(function () {})
    ]);
    AbstractClass.super_ = Object.prototype;
    return AbstractClass;
});

var ParentClass = class_(function(){
    function ParentClass () {}
    extend_(ParentClass, AbstractClass);
    implementation_(ParentClass, [
          constructor_(function (initVar) {
                var instance = new ParentClass();
                instance.initVar = initVar;
                var privateVar = 'ParentClass private var';
                function privateFunction () {return 'ParentClass private function can show ' + privateVar + ' and ' + instance._privateMethod();}
                instance.eventFunction = function () {alert(privateFunction());}
                document.addEventListener('click', instance.eventFunction, false);
                return instance;
          })
        , destructor_(function () {
                document.removeEventListener('click', this.eventFunction, false);
                console.log('ParentClass destroyed.');
          })
        , default_('defaultVar', 'ParentClass default var.')
        , public_('publicMethod', function () {return 'ParentClass public method.';})
        , private_('_privateMethod', function () {return 'ParentClass private method.';})
        , final_('finalMethod', function () {return 'ParentClass final method.';})
        , static_('staticMethod', function () {return 'ParentClass static method.';})
    ]);
    return ParentClass;
});

var parentObject = ParentClass.new_('ParentClass init var');
console.log(parentObject.constructor.name);
console.log(parentObject.defaultVar);
console.log(parentObject.initVar);
console.log(parentObject.publicMethod());
console.log(parentObject.finalMethod());
console.log(ParentClass.staticMethod());
setTimeout(function(){parentObject.destroy_(); parentObject = null;}, 5000);

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

var MixinClass = class_(function(){
    function MixinClass () {}
    implementation_(MixinClass, [
          public_('mixinPublicMethod', function () {return 'MixinClass public method.';})
        , static_('mixinStaticMethod', function () {return 'MixinClass static method.';})
    ]);
    return MixinClass;
});

var ChildClass = class_(function(){
    function ChildClass () {}
    extend_(ChildClass, ParentClass);
    implementation_(ChildClass, [
          constructor_(function (initVar) {
                var instance = new ChildClass();
                instance.initVar = initVar;
                return instance;
          })
        , destructor_(function () {console.log('ChildClass destroyed.');})
        , override_('publicMethod', function () {return 'ChildClass public method.';})
    ]);
    mixin_(ChildClass, MixinClass);
    return ChildClass;
});

var childObject = ChildClass.new_('ChildClass init var');
console.log(childObject.constructor.name);
console.log(childObject.defaultVar);
console.log(childObject.initVar);
console.log(childObject.publicMethod());
console.log(childObject.finalMethod());
console.log(ChildClass.staticMethod());
console.log(childObject.mixinPublicMethod());
console.log(ChildClass.mixinStaticMethod());
setTimeout(function(){childObject.destroy_(); childObject = null;}, 5000);

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

var SingletonClassInterface = interface_(function(){
    function SingletonClassInterface () {}
    implementation_(SingletonClassInterface, [
          singleton_()
        , constructor_()
        , destructor_()
        , default_('defaultVar')
        , public_('publicMethod')
        , private_('_privateMethod')
        , final_('finalMethod')
        , static_('staticMethod')
        , public_('mixinPublicMethod')
        , static_('mixinStaticMethod')
    ]);
    return SingletonClassInterface;
});

var SingletonClass = class_(function(){
    function SingletonClass () {}
    extend_(SingletonClass, ChildClass);
    implementation_(SingletonClass, [
          singleton_()
        , constructor_(function (initVar) {
                if (SingletonClass.instance_) {return SingletonClass.instance_;}
                var instance = create_singleton_(SingletonClass);
                instance.initVar = initVar;
                return instance;
          })
        , destructor_(function () {console.log('SingletonClass destroyed.');})
    ]);
    impliment_(SingletonClass, SingletonClassInterface);
    return SingletonClass;
});

var singletonObject1 = SingletonClass.new_('SingletonClass init var 1');
var singletonObject2 = SingletonClass.new_('SingletonClass init var 2');
console.log('Is singleton? ' + (singletonObject1 === singletonObject2));
console.log(singletonObject1.constructor.name);
console.log(singletonObject1.defaultVar);
console.log(singletonObject1.initVar);
console.log(singletonObject1.publicMethod());
console.log(singletonObject1.finalMethod());
console.log(SingletonClass.staticMethod());
console.log(singletonObject1.mixinPublicMethod());
console.log(SingletonClass.mixinStaticMethod());
setTimeout(function(){singletonObject1.destroy_(); singletonObject1 = null; singletonObject2 = null;}, 5000);

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

function Enum () {
    var i, len = arguments.length, allValues;
    for (i = 0; i < len; i++) {
        this[arguments[i]] = i;
    }
    this.allValues = Array.prototype.slice.call(arguments);
}

Enum.prototype.values = function () {
    return this.allValues;
};

var Color = new Enum('RED', 'GREEN', 'BLUE');

console.log(Color.RED);
console.log(Color.GREEN);
console.log(Color.BLUE);
console.log(Color.values());

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

function class_2 (cls) {
    var classFunction;
    function AbstractClass () {}
    AbstractClass.new_ = function () {var instance = new AbstractClass(); return instance;};
    AbstractClass.prototype.destroy = function () {};
    AbstractClass.super_ = Object.prototype;
    if (cls && cls.name_ && cls.body_) {
        classFunction = (new Function('return function ' + cls.name_ + ' () {}'))();
        if (cls.extends_) {extend_(classFunction, cls.extends_);} else {extend_(classFunction, AbstractClass);}
        implementation_(classFunction, cls.body_);
        if (cls.mixin_) {mixin_(classFunction, cls.mixin_);}
        if (cls.impements_) {impliment_(classFunction, cls.impements_);}
        return classFunction;
    } else {
        throw new Error('Class name and body must be specified.');
    }
}

function interface_2 (intfc) {
    return class_2(intfc);
}

var SingletonClassInterface2 = interface_2({
      name_: 'SingletonClassInterface2'
    , extends_: SingletonClassInterface
    , body_: [
          singleton_()
        , constructor_()
        , destructor_()
        , default_('defaultVar')
        , public_('publicMethod')
        , private_('_privateMethod')
        , final_('finalMethod')
        , static_('staticMethod')
        , public_('mixinPublicMethod')
        , static_('mixinStaticMethod')
    ]
});

var ParentClass2 = class_2({
      name_: 'ParentClass2'
    , extends_: ParentClass
    , impements_: SingletonClassInterface2
    , mixin_: MixinClass
    , body_: [
          singleton_()
        , constructor_(function (initVar) {
                if (ParentClass2.instance_) {return ParentClass2.instance_;}
                var instance = create_singleton_(ParentClass2);
                instance.initVar = initVar;
                return instance;
          })
        , destructor_(function () {console.log('ParentClass2 destroyed.');})
        , override_('publicMethod', function () {return 'ParentClass2 public method.';})
      ]
});

var singletonParentObject21 = ParentClass2.new_('Singleton ParentClass2 init var 1');
var singletonParentObject22 = ParentClass2.new_('Singleton ParentClass2 init var 2');
console.log('Is singleton? ' + (singletonParentObject21 === singletonParentObject22));
console.log(singletonParentObject21.constructor.name);
console.log(singletonParentObject21.defaultVar);
console.log(singletonParentObject21.initVar);
console.log(singletonParentObject21.publicMethod());
console.log(singletonParentObject21.finalMethod());
console.log(ParentClass2.staticMethod());
console.log(singletonParentObject21.mixinPublicMethod());
console.log(ParentClass2.mixinStaticMethod());
setTimeout(function(){singletonParentObject21.destroy_(); singletonParentObject21 = null; singletonParentObject22 = null;}, 5000);

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

function ClassicalClass (initVar) {
    this.initVar = initVar;
}

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

ClassicalClass.prototype = {
      defaultVar: 'ClassicalClass default var.'
    , publicMethod: function () {return 'ClassicalClass public method.';}
    , _privateMethod: function () {return 'ClassicalClass private function can show ' + this.initVar;}
    , destroy: function () {console.log('ClassicalClass destroyed.');}
    , constructor: ClassicalClass
}

var classicalObject = new ClassicalClass('ClassicalClass init var.');
console.log(classicalObject.constructor.name);
console.log(classicalObject.defaultVar);
console.log(classicalObject.initVar);
console.log(classicalObject.publicMethod());
console.log(classicalObject._privateMethod());
console.log(ClassicalClass.staticMethod());
setTimeout(function(){classicalObject.destroy(); classicalObject = null;}, 5000);

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

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

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

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