среда, 20 ноября 2013 г.

JavaScript Очень простой пример наследования классов от Microsoft из TypeScript

Базовое наследование классов в JavaScript.

var extend = this.extend || function (childClass, parentClass) {

    for (var key in parentClass) {
        if (parentClass.hasOwnProperty(key)) {
            childClass[key] = parentClass[key];
        }
    }

    function F () {
        this.constructor = childClass;
    }

    F.prototype = parentClass.prototype;

    childClass.prototype = new F();

};

var ParentClass = (function () {

    // Constructor
    function ParentClass(message) {
        // Constructor body
        this.greeting = message;
        this.x = 1;
        this.y = 2;
        this.z = 3;
    }

    // Public function
    ParentClass.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };

    return ParentClass;

})();

var ChildClass = (function (parentClass) {

    // Extend
    extend(ChildClass, parentClass);

    // Constructor
    function ChildClass(message) {
        parentClass.apply(this, arguments); // или можно сделать частично parentClass.call(this, message);
        this.greeting = message;
    }

    // Public variable
    ChildClass.prototype.name = 'Parent';

    // Public function
    ChildClass.prototype.greet = function () {
        return 'Hello, ' + this.greeting;
    };

    // Public function
    ChildClass.prototype.hey = function () {
        return 'Hey! ' + privateFunc();
    };

    // Private variable
    var privateVar = 'Hello';

    // Private function
    function privateFunc () {
        return privateVar + ' my friend!';
    }

    // Static variable
    ChildClass.staticVar = 'I am static variable';

    // Static function
    function staticFunc () {
        return 'I am static function';
    }

    return ChildClass;

})(ParentClass);

var SubClass = (function (parentClass) {

    extend(SubClass, parentClass);

    function SubClass(message) {
        parentClass.apply(this, arguments);
        this.greeting = message;
    }

    SubClass.prototype.greet = function () {
        return 'Hello, ' + this.greeting;
    };

    SubClass.prototype.hey = function () {
        return 'Hey!';
    };

    return SubClass;

})(ChildClass);

var parentObject = new ParentClass('world');
var childObject = new ChildClass();
var subObject = new SubClass();

console.log(parentObject.greet());
console.log(childObject.hey());
console.log(subObject.hey());

Примеси - Mixins

function applyMixins(derivedCtor, baseCtors) {
    baseCtors.forEach(function (baseCtor) {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(function (name) {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
    });
}

// Disposable Mixin
var Disposable = (function () {
    function Disposable() {
    }
    Disposable.prototype.dispose = function () {
        this.isDisposed = true;
    };
    return Disposable;
})();

// Activatable Mixin
var Activatable = (function () {
    function Activatable() {
    }
    Activatable.prototype.activate = function () {
        this.isActive = true;
    };
    Activatable.prototype.deactivate = function () {
        this.isActive = false;
    };
    return Activatable;
})();

var SmartObject = (function () {
    function SmartObject() {
        var _this = this;
        // Disposable
        this.isDisposed = false;
        // Activatable
        this.isActive = false;
        setInterval(function () {
            return console.log(_this.isActive + " : " + _this.isDisposed);
        }, 500);
    }
    SmartObject.prototype.interact = function () {
        this.activate();
    };
    return SmartObject;
})();

applyMixins(SmartObject, [Disposable, Activatable]);

var smartObj = new SmartObject();

setTimeout(function () {
    return smartObj.interact();
}, 1000);

Пример работы со статичными переменными и функциями внутри класса.

var Point = (function () {

    // Object constructor
    function Point(x, y) {
        this.x = x;
        this.y = y;
    }

    // Public function
    Point.prototype.distance = function (p) {
        var dx = this.x - p.x;
        var dy = this.y - p.y;
        return Math.sqrt(dx * dx + dy * dy);
    };

    // Static function
    Point.distance = function (p1, p2) {
        return p1.distance(p2);
    };

    // Static variable
    Point.origin = new Point(0, 0);

    return Point;

})();

Пример вызова родительского класса в дочернем классе.

var Point = (function () {

    // Constructor
    function Point(x, y) {
        this.x = x;
        this.y = y;
    }

    // Public function
    Point.prototype.toString = function () {
        return 'x=' + this.x + ' y=' + this.y;
    };

    return Point;

})();

var ColoredPoint = (function (parentClass) {

    // Extend parent class
    extend(ColoredPoint, parentClass);

    // Constructor
    function ColoredPoint(x, y, color) {
        parentClass.apply(this, arguments);
        parentClass.call(this, x, y);
        this.color = color;
    }

    // Public function
    ColoredPoint.prototype.toString = function () {
        return parentClass.prototype.toString.call(this) + ' color=' + this.color;
    };

    return ColoredPoint;

})(Point);

Пример наследования класса во множестве дочерних классов.

var Control = (function () {

    function Control() {}

    return Control;

})();

var Button = (function (parentClass) {

    extend(Button, parentClass);

    function Button() {
        parentClass.apply(this, arguments);
    }

    Button.prototype.select = function () {};

    return Button;

})(Control);

var TextBox = (function (parentClass) {

    extend(TextBox, parentClass);

    function TextBox() {
        parentClass.apply(this, arguments);
    }

    TextBox.prototype.select = function () {};

    return TextBox;

})(Control);

var Image = (function (parentClass) {

    extend(Image, parentClass);

    function Image() {
        parentClass.apply(this, arguments);
    }

    return Image;

})(Control);

Public, Private, и Privileged переменные и функции.

Public

function Constructor(...) {
    this.membername = value;
}

Constructor.prototype.membername = value;

Private

function Constructor(...) {
    var that = this;

    // Private
    var membername = value;
    function membername(...) {...}
}

Примечание: данное выражение

function membername(...) {...}

это сокращенная запись такого выражения

var membername = function membername(...) {...};

Privileged

function Constructor(...) {
    this.membername = function (...) {...};
}

Пример.

function Constructor (param) {

    // Public variable
    this.member = param;

    // Private variables. They are not accessible to the outside, nor are they accessible to the object's own public methods. They are accessible to private functions.
    var secret = 3;
    var that = this;

    // Private function inside constructor. Private methods cannot be called by public methods. It can be used to make this object limited to 3 uses. To make private methods useful, we need to introduce a privileged method.
    function dec() {
        if (secret > 0) {
            secret -= 1;
            return true;
        } else {
            return false;
        }
    }

    // Privileged function. A privileged method is able to access the private variables and methods, and is itself accessible to the public methods and the outside. It is possible to delete or replace a privileged method, but it is not possible to alter it, or to force it to give up its secrets.
    this.service = function () {
        return dec() ? that.member : null;
    };

}

Пример модуля.

var M;

(function (M) {

    var s = "hello";

    function f() {
        return s;
    }

    M.f = f;

})(M || (M = {}));

M.f();

Пример экспортирования значений в модуль.

(function (exports) {

    var key = generateSecretKey();

    function sendMessage(message) {
        sendSecureMessage(message, key);
    }

    exports.sendMessage = sendMessage;

})(MessageModule);

Загрузка модулей осуществляется в стиле Require JS.

define(["require", "exports", "log"], function(require, exports, log) {
    log.message("hello");
});

Пояснения.

Наследование свойств, определенный в конструкторе класса через this.

var Parent = function(){
    this.surname = 'Brown';
};

var Child = function(){
    Parent.apply(this, arguments);
};

var boy = new Child();

console.log(boy.surname); // Brown

Выше написанный код равносилен следующему коду.

Наследование свойств, определенный в prototype класса через prototype.

function inherit (proto) {
    function F() {}
    F.prototype = proto;
    return new F();
}

Parent.prototype.surname = 'Brown';

var Child = function(){};

Child.prototype = inherit(Parent.prototype);

var boy = new Child();

console.log(boy.surname); // Brown

Альтернативная запись функции наследования классов (менее предпочтительная, чем в TypeScript).

function extend(Child, Parent) {
  Child.prototype = inherit(Parent.prototype);
  Child.prototype.constructor = Child;
  Child.parent = Parent.prototype;
}

function inherit(proto) {
  function F() {}
  F.prototype = proto;
  return new F();
}

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

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