akinuri
akinuri

Reputation: 12027

Access constructor from prototype method

In the code below, I have a Mass constructor and some methods. Originally, methods were inside the Mass constructor, but there are many methods I'm using. So, to keep things more organized I removed some of the methods outside of the Mass and add them using prototype.

But then I have an issue. I can't reference to Mass with this; it refers to window.

function Mass(elm) {
    this.getElement         = function(element) {
        if (typeof element == "string") {
            return document.getElementById(element);
        }
        return element;
    };
    this.elm                = this.getElement(elm);
    this.elm.style.position = "absolute";
    this.elm.style.left     = 0 + "px";
    this.elm.style.top      = 0 + "px";
    this.updateVars         = function () {
            this.width      = parseInt(this.elm.style.width, 10);
            this.height     = parseInt(this.elm.style.height, 10);
            this.top        = parseInt(this.elm.style.top, 10);
            this.left       = parseInt(this.elm.style.left, 10);
            this.radius     = this.width / 2;
            this.originX    = this.left + this.radius;
            this.originY    = this.top + this.radius;
            this.worldOrgX  = this.originX + parseInt(this.elm.parentNode.style.left, 10);
            this.worldOrgY  = this.originY + parseInt(this.elm.parentNode.style.top, 10);
    };
}

Mass.prototype = {
    // other methods here
    rotation    : {
         // this = window
        mass    : this,
        angle   : 0,
        handler : null,
        rotate  : function (angularVelocity, rotationSpeed) {
                    this.angle = (this.angle + angularVelocity) % 360;
                    // here I need to access Mass.elm and apply rotate
                    this.mass.elm.style.webkitTransform = "rotate(" + this.angle + "deg)";
                },
        start   : function (angularVelocity, rotationSpeed) {
                    var rotation = this; // this = Mass.rotation
                    rotation.handler = setInterval(function () {
                        rotation.rotate(angularVelocity, rotationSpeed);
                    }, rotationSpeed);
                },

    },
}

var earth   = new Mass("earth");
//earth.rotation.start(4.5, 25);

Fiddle

Old version of the code. This is working fine. What changes should I make for new one to work?

Upvotes: 1

Views: 76

Answers (1)

Aadit M Shah
Aadit M Shah

Reputation: 74204

The prototype is not meant to make the code inside your constructor smaller. The prototype is meant to share common functionality. For example your getElement method is the same for all instances of Mass. Hence it would be ideal to put it on the prototype. Same for updateVars.

On the other hand this.rotation is an object and not a function. Every instance of Mass will have a different this.rotation property. Hence this.rotation cannot be shared and hence must not be put on the prototype.

I usually like to create "classes" in JavaScript using a simple defclass function:

function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

The defclass function unwraps a prototype and returns its constructor. By the way, the constructor is just another method on the prototype.

So here's how I would rewrite your Mass class:

var Mass = defclass({
    constructor: function (id) {
        var element = this.element = this.getElement(id);
        var style = element.style;

        style.position = "absolute";
        style.left = 0;
        style.top = 0;

        this.rotation = new Rotation(this);
    },
    getElement: function (id) {
        if (typeof id !== "string") return id;
        else return document.getElementById(id);
    }
});

Next we create the Rotation class:

var Rotation = defclass({
    constructor: function (mass) {
        this.mass = mass;
        this.angle = 0;
    },
    start: function (angularVelocity, delay) {
        var rotation = this;

        setInterval(function () {
            rotation.rotate(angularVelocity);
        }, delay);
    },
    rotate: function (angularVelocity) {
        var angle = this.angle = (this.angle + angularVelocity) % 360;
        var transform = "rotate(" + angle + "deg)";
        var style = this.mass.element.style;

        style.webkitTransform = transform;
        style.mozTransform = transform;
        style.msTransform = transform;
        style.oTransform = transform;
        style.transform = transform;
    }
});

Finally we simply create an instance of Mass and make it rotate:

var earth = new Mass("earth");
earth.rotation.start(4.5, 25);

See the demo for yourself: http://jsfiddle.net/NL3SJ/

Upvotes: 1

Related Questions