четверг, 25 декабря 2014 г.

Полная имитация традиционных классов в JavaScript включая паттерн синглтон и примеси. Версия 2.0.

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

//----------------------------------------------------------------------------

// Наследование дочерним классом всех свойств и методов из родительского класса
var extend = this.extend || function (childClass, parentClass) {
    // Копируем в дочерний класс статичные свойства и методы из родительского класса
    for (var key in parentClass) {
        if (parentClass.hasOwnProperty(key)) {childClass[key] = parentClass[key];}
    }
    // Копируем в дочерний класс свойства и методы из прототипа родительского класса
    function F () {} // Временная функция
    F.prototype = parentClass.prototype; // Копируем свойства и методы прототипа из родителя во временную функцию
    childClass.prototype = new F(); // Присваиваем дочернему прототипу объект, созданный из родительского класса
    childClass.prototype.constructor = childClass; // Ставим ссылку конструктора на дочерний класс
    childClass.__super__ = parentClass.prototype; // Ссылка на родительский класс для непосредственного вызова родительских методов, если потребуется.
    // __super__ можно использовать в методе дочернего класса, вызывая дополнительно метод родительского класса внутри него так: ChildClass.__super__.someMethod.call(this, 'some data');
};

// Копирование свойств и методов из примесей в класс
var mixin = this.mixin || function (childClass) {
    var mixins = Array.prototype.slice.call(arguments, 1) // Классы с набором методов mixin
        , i
        , len = mixins.length
        , key;
    if (len) {
        // Для каждого элемента из массива mixins копируем статичные свойства и методы в дочерний класс
        for (i = 0; i < len; i++) {
            // Копируем в класс статичные свойства и методы из примеси
            for (key in mixins[i]) {
                if (mixins[i].hasOwnProperty(key)) {childClass[key] = mixins[i][key];}
            }
            // Копируем в прототип класса свойства и методы из прототипа примеси
            for (key in mixins[i].prototype) {
                childClass.prototype[key] = mixins[i].prototype[key];
            }
        }
    }
};

//----------------------------------------------------------------------------

var AbstractClass = (function(){

    // Constructor
    function AbstractClass () {}
 
    // Initializer (getInstance)
    AbstractClass.newInstance = function () {
        var instance = new AbstractClass();
        return instance;
    };
 
 
    // Parent class link
    AbstractClass.__super__ = Object.prototype;
 
    // Destroyer (destroy)
    AbstractClass.prototype.deleteInstance = function (referenceName) {
        // Удаление экземпляра класса из памяти (необяхательно нужно)
        eval(
                referenceName + '= null;' // Устанавливая referenceName равным null, мы удаляем ссылку на объект
            + ' delete ' + referenceName + ';' // далее можем удалить и сам объект
        );
    };

    return AbstractClass;

})();

//----------------------------------------------------------------------------

// AbstractClass Test

var abstractObject = AbstractClass.newInstance();

console.log(abstractObject.constructor.name);

console.log(AbstractClass.__super__);

// abstractObject.deleteInstance('abstractObject'); // Удаление ссылки на объект из JavaScript

console.log('AbstractClass instance: ' + abstractObject);

//----------------------------------------------------------------------------

var ParentClass = (function(parentClass){

    // Extend
    extend(ParentClass, parentClass);

    // Constructor
    function ParentClass () {}
 
    // Initializer
    ParentClass.newInstance = function (initVar, initMethod) {
        // Instance
        var instance = new ParentClass();
 
        // Init public
        instance.initVar = initVar;
        instance.initMethod = initMethod;
     
        // Privilege public
        instance.privilegeVar = 'ParentClass privilege public variable.';
        instance.privilegeMethod = function () {return 'ParentClass privilege public method.' + ' ' + privateVar + ' ' + privateFunction();};
     
        // Private
        var privateVar = 'ParentClass private variable.';
        function privateFunction () {return 'ParentClass private function.';}
     
        // Events
        instance.eventFunction = function () {instance._privateMethod(instance);}
        document.addEventListener('click', function(){alert('This event can\'t be remove.' + ' ' + instance.privilegeVar);}, false);
        document.addEventListener('click', instance.eventFunction, false);
     
        // Return instance
        return instance;
    };
 
    // Destroyer
    ParentClass.prototype.deleteInstance = function (referenceName) {
        document.removeEventListener('click', this.eventFunction, false);
        // Удаление экземпляра класса из памяти (необяхательно нужно)
        eval(
                referenceName + '= null;' // Устанавливая referenceName равным null, мы удаляем ссылку на объект
            + ' delete ' + referenceName + ';' // далее можем удалить и сам объект
        );
    };

    // Default value - будет браться значение по умолчанию, если данное свойство будет удалено или не будет определено
    ParentClass.prototype.initVar = 'ParentClass default variable.';
 
    // Private
    ParentClass.prototype._privateMethod = function (instance) {
        alert('This event can be remove.' + ' ' + instance.privilegeVar);
    };

    // Public
    ParentClass.prototype.publicMethod = function () {
        return 'ParentClass public method.' + ' ' + this.privilegeVar;
    };
 
    // Static
    ParentClass.staticVar = 'ParentClass static variable.';
    ParentClass.staticMethod = function () {return 'ParentClass static method.';};

    return ParentClass;

})(AbstractClass);

//----------------------------------------------------------------------------

// ParentClass Test

var parentObject = ParentClass.newInstance('ParentClass init public variable.', function () {return 'ParentClass init public method.' + ' ' + this.initVar;});

console.log(parentObject.constructor.name);

console.log(ParentClass.__super__);

console.log(parentObject.initMethod());

console.log(parentObject.privilegeVar);
console.log(parentObject.privilegeMethod());

parentObject._privateMethod(parentObject);

console.log(parentObject.publicMethod());

console.log(ParentClass.staticVar);
console.log(ParentClass.staticMethod());

setTimeout(function (){parentObject.deleteInstance('parentObject'); console.log('ParentClass instance: ' + parentObject);}, 5000);

//----------------------------------------------------------------------------

var MixinClass = (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 = (function(parentClass){

    // Extend
    extend(ChildClass, parentClass);

    // Constructor
    function ChildClass () {}
 
    // Initializer
    ChildClass.newInstance = function (initVar, initMethod) {
        // Instance
        var instance = new ChildClass();
     
        // Parent Constructor Call (Несовместимо с Mixin)
        // instance = parentClass.newInstance.apply(instance, arguments);

        // Init public
        instance.initVar = initVar;
        instance.initMethod = initMethod;
     
        // Privilege public
        instance.privilegeVar = 'ChildClass privilege public variable.';
        instance.privilegeMethod = function () {return 'ChildClass privilege public method.' + ' ' + privateVar + ' ' + privateFunction();};
     
        // Private
        var privateVar = 'ChildClass private variable.';
        function privateFunction () {return 'ChildClass private function.';}
     
        // Events
        instance.eventFunction = function () {instance._privateMethod(instance);}
        document.addEventListener('click', function(){alert('This event can\'t be remove.' + ' ' + instance.privilegeVar);}, false);
        document.addEventListener('click', instance.eventFunction, false);

        // Call parentClass method
        console.log(ChildClass.__super__.publicMethod.call(instance));
        console.log(parentClass.staticMethod());
     
        // Return instance
        return instance;
    };

    // Override Public
    ChildClass.prototype.publicMethod = function () {
        return 'ChildClass public method.' + ' ' + this.privilegeVar;
    };
 
    // Mixin
    mixin(ChildClass, MixinClass);
 
    return ChildClass;

})(ParentClass);

//----------------------------------------------------------------------------

// ChildClass Test

var childObject = ChildClass.newInstance('ChildClass init public variable.', function () {return 'ChildClass init public method.' + ' ' + this.initVar;});

console.log(childObject.constructor.name);

console.log(ChildClass.__super__);

console.log(childObject.initMethod());

console.log(childObject.privilegeVar);
console.log(childObject.privilegeMethod());

childObject._privateMethod(childObject);

console.log(childObject.publicMethod());

console.log(ChildClass.staticVar);
console.log(ChildClass.staticMethod());

console.log(childObject.mixinPublicMethod());
console.log(ChildClass.mixinStaticMethod());

setTimeout(function (){childObject.deleteInstance('childObject'); console.log('ChildClass instance: ' + childObject);}, 5000);

//----------------------------------------------------------------------------

var SingletonClass = (function(parentClass){

    // Extend
    extend(SingletonClass, parentClass);

    // Constructor
    function SingletonClass () {}
 
    // Static instance
    SingletonClass.instance = undefined;
 
    // Initializer
    SingletonClass.newInstance = function (initVar, initMethod) {
 
        // Return static singleton instance if singleton object exists
        if (SingletonClass.instance) {return SingletonClass.instance;}
 
        // Instance
        var instance = new SingletonClass();
     
        SingletonClass.instance = instance;

        // Init public
        instance.initVar = initVar;
        instance.initMethod = initMethod;
     
        // Privilege public
        instance.privilegeVar = 'SingletonClass privilege public variable.';
        instance.privilegeMethod = function () {return 'SingletonClass privilege public method.' + ' ' + privateVar + ' ' + privateFunction();};
     
        // Private
        var privateVar = 'SingletonClass private variable.';
        function privateFunction () {return 'SingletonClass private function.';}
     
        // Events
        instance.eventFunction = function () {instance._privateMethod(instance);}
        document.addEventListener('click', function(){alert('This event can\'t be remove.' + ' ' + instance.privilegeVar);}, false);
        document.addEventListener('click', instance.eventFunction, false);
     
        // Return instance
        return instance;
    };

    return SingletonClass;

})(ChildClass);

//----------------------------------------------------------------------------

// SingletonClass Test

var firstSingletonObject = SingletonClass.newInstance('First SingletonClass init public variable.', function () {return 'First SingletonClass init public method.' + ' ' + this.initVar;});
var secondSingletonObject = SingletonClass.newInstance('Second SingletonClass init public variable.', function () {return 'Second SingletonClass init public method.' + ' ' + this.initVar;});
var thirdSingletonObject = SingletonClass.newInstance('Third Second SingletonClass init public variable.', function () {return 'Third SingletonClass init public method.' + ' ' + this.initVar;});

console.log('Is singleton? ' + (firstSingletonObject === secondSingletonObject && firstSingletonObject === thirdSingletonObject && secondSingletonObject === thirdSingletonObject).toString());

console.log(thirdSingletonObject.constructor.name);

console.log(SingletonClass.__super__);

console.log(thirdSingletonObject.initMethod());

console.log(thirdSingletonObject.privilegeVar);
console.log(thirdSingletonObject.privilegeMethod());

thirdSingletonObject._privateMethod(thirdSingletonObject);

console.log(thirdSingletonObject.publicMethod());

console.log(SingletonClass.staticVar);
console.log(SingletonClass.staticMethod());

setTimeout(function (){thirdSingletonObject.deleteInstance('thirdSingletonObject'); console.log('SingletonClass instance: ' + thirdSingletonObject);}, 5000);

//----------------------------------------------------------------------------

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

понедельник, 15 декабря 2014 г.

Object и Array deepCopy() или Clone()

function deepCopy (obj) {
    if (Object.prototype.toString.call(obj) === '[object Array]') {
        var out = [], i = 0, len = obj.length;
        for ( ; i < len; i++ ) {
            out[i] = deepCopy(obj[i]);
        }
        return out;
    }
    if (typeof obj === 'object') {
        var out = {}, i;
        for ( i in obj ) {
            out[i] = deepCopy(obj[i]);
        }
        return out;
    }
    return obj;
}

Определение типа: function classof ()

function classof (o) {
    if (o === null) {return 'Null';}
    if (o === undefined) {return 'Undefined';}
    if (o !== o) {return 'NaN';}
    return Object.prototype.toString.call(o).slice(8, -1);
}

console.log(classof(null));
console.log(classof(undefined));
console.log(classof({}));
console.log(classof([]));
console.log(classof(''));
console.log(classof(0));