Search code examples
javascriptprototype

Adding properties to functions and also setting their prototype


I have to work on the following piece of code:

function Vector(x, y) {
    this.x = x || 0;
    this.y = y || 0;
}

Vector.add = function(a, b) {
    return new Vector(a.x + b.x, a.y + b.y);
};

Vector.sub = function(a, b) {
    return new Vector(a.x - b.x, a.y - b.y);
};

Vector.scale = function(v, s) {
    return v.clone().scale(s);
};

Vector.random = function() {
    return new Vector(
        Math.random() * 2 - 1,
        Math.random() * 2 - 1
    );
};

Vector.prototype = {
    set: function(x, y) {
        if (typeof x === 'object') {
            y = x.y;
            x = x.x;
        }
        this.x = x || 0;
        this.y = y || 0;
        return this;
    },

    add: function(v) {
        this.x += v.x;
        this.y += v.y;
        return this;
    },

    sub: function(v) {
        this.x -= v.x;
        this.y -= v.y;
        return this;
    },

    scale: function(s) {
        this.x *= s;
        this.y *= s;
        return this;
    },

    length: function() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    },

    lengthSq: function() {
        return this.x * this.x + this.y * this.y;
    },

    normalize: function() {
        var m = Math.sqrt(this.x * this.x + this.y * this.y);
        if (m) {
            this.x /= m;
            this.y /= m;
        }
        return this;
    },

    angle: function() {
        return Math.atan2(this.y, this.x);
    },

    angleTo: function(v) {
        var dx = v.x - this.x,
            dy = v.y - this.y;
        return Math.atan2(dy, dx);
    },

    distanceTo: function(v) {
        var dx = v.x - this.x,
            dy = v.y - this.y;
        return Math.sqrt(dx * dx + dy * dy);
    },

    distanceToSq: function(v) {
        var dx = v.x - this.x,
            dy = v.y - this.y;
        return dx * dx + dy * dy;
    },

    lerp: function(v, t) {
        this.x += (v.x - this.x) * t;
        this.y += (v.y - this.y) * t;
        return this;
    },

    clone: function() {
        return new Vector(this.x, this.y);
    },

    toString: function() {
        return '(x:' + this.x + ', y:' + this.y + ')';
    }
};

This piece of code has a function Vector and to this function there is a property attached which is Vector.add. However, a few lines down there is Vector.prototype which then in turn defines add.

So at this point we have Vector with Vector.add and Vector.prototype.add and I am not quite sure what is the difference when calling these.

For full reference the code I am trying to redo is from the Gravity Points CodePen so you can view the whole code as well as its usage.

However, to me, as someone who has mainly used ECMAScript 6, this code is looking very strange, to say the least.


Solution

  • For more info on static vs prototype methods, you can read this answer here. In your case though:

    Static call returns a new Vector by adding two vectors together. v1 and v2 remain unchanged.

    let v1 = new Vector(1, 1);
    let v2 = new Vector(2, 2);
    let v3 = Vector.add(v1, v2);
    
    v1.toString(); // (x:1, y:1)
    v2.toString(); // (x:2, y:2)
    v3.toString(); // (x:3, y:3)
    

    Prototype call adds two vectors together but does not create a new Vector; instead the x and y properties of v2 are added onto v1 in place.

    let v1 = new Vector(1, 1);
    let v2 = new Vector(2, 2);
    v1.add(v2);
    
    v1.toString(); // (x:3, y:3)
    v2.toString(); // (x:2, y:2)
    

    Note the prototype method returns a reference to the instance it was called on so subsequent calls can be chained:

    v1.add(v2).sub(v3).add(v4).toString();