3

[Edited: vector2d and vector3d are not good examples. I am now using pt and ptMass instead.]

I have been searching answers for this but there seems no good solution available.

Suppose I have an object as below.

function pt(x,y){
    this.x = x;
    this.y = y;
}

Now, I would like to have a point mass as below.

function ptMass(x,y,m){
    this.x = x;
    this.y = y;
    this.m = m;
}

It is better to use inheritance to create the ptMass class.

I am currently using the following way to do it.

function ptMass(x,y,m){
    pt.apply(this.arguments);
    this.m = m;
}

Is there a way of doing this with prototype? I have tried the following but it doesn't work.

pt = function(m){this.m = m};
ptMass.prototype = new pt();

One more question, what is the advance of using prototypical inheritance?

  • 1
    i dont like your approach, or at least not in your specific sample, since it violates the basic principle: a "verctor 3d" is not a "vector 2d". I think that what you are looking is parametrization – jodarove Nov 29 '12 at 15:57

2 Answers2

3

First, why is it better to inherit it?
I get several arguments for inheritance, though I prefer composition (modules/components and dependency-injection) myself.

Second, you get reuse out of doing:

function Vector2D (x, y) {
    this.x = x;
    this.y = y;
}


function Vector3D (x, y, z) {
    Vector2D.call(this, x, y);
    this.z = z;
}

Third, in a large-scale JS app, creating tens of thousands (or millions) of polygons, the extra function calls are just going to slow it down, unnecessarily.

Fourth, you can't use

Vector3D.prototype = new Vector2D();

First, you're not initializing x and y to anything inside of the Vector2D.
Second, prototype is meant to hold STATIC properties and functions, if you're coming from other languages.

Even if you did initialize Vector2D, it would still be the exact same Vector2D for every instance of your Vector3D class.

Why not just:

var point2D = function (x, y) {
        return { x : x,
                 y : y  };
    },
    point3D = function (x, y, z) {
        return { x : x,
                 y : y,
                 z : z };
    };

...and when you have those particular building-blocks as your base elements, compose different modules which use them?


EDIT

function anObj (x, y) {
    var privateFunc = function () { return y; };

    return {
        x : x,
        method : function () { return privateFunc(); }
    };
}


var myObj = anObj(1, 2);
myObj.x;        // 1
myObj.method(); // 2

The upside here is that you've now got private variables (privateFunc and y).
The downside is that in terms of memory-usage, each instance of your object needs to have its OWN copy of any private methods.
So if you're making hundreds of thousands of these objects (vertices/polygons, for example), and they don't NEED to have private state, this isn't the method to use.

If, however, you're making players or enemies, or controllers, or anything which you DON'T want tampered with, then you DO want to use a method like this (or more advanced).

If you have a situation where you might want STATIC data / methods which are also 100% private, then you should try a format like this:

var objectMaker = (function () {
    var staticData = 32,
        staticFunc = function (num) { return num + staticData; };

    return function (x, y) {
        var privateData = 12,
            privateMethod = function (num) {
                return y + privateData + staticFunc(num);
            };

        return { x : x,
                 method : function (num) { return privateMethod(num); }
        };
    };
}());


var myObj = objectMaker(3, 4);
myObj.x;          // 3;
myObj.method(12); // 12 + y(y === 4) + privateData + staticData;

So what we've done here is we've got an immediately-firing function (it fires as soon as its defined, and returns the value of the function to the variable).
So in our particular case, this function immediately returns the actual function that we want to use to make new instances.

The private variables and functions that are inside of the immediate function (not the constructor we return) are static, in that every instance you create will have access to the exact same functions/data inside of that closure.
The downside is that those static functions have NO access to private (or even instance-specific) data.
This means that you have to pass your values into the static function, and catch the return from the static function, as you can't rely on the static methods to modify instance values (technically, you can modify objects/arrays, directly if you pass them into the function, but otherwise you must catch return values).

Now you can have plenty of helper functions which are shared amongst instances (lower memory), while still being private and secure.

If what you want is public properties, public methods, and static methods/properties, THEN you can access them like so:

var Obj = function (x, y) {
    this.x = x;
    this.per_instance_method = function () { return y; };
};

Obj.prototype.staticData = { z : 32 };
Obj.prototype.staticMethod = function () { return this.x + this.staticData.z; };

var myObj = new Obj(3, 4);
myObj.staticMethod();

...just don't expect any prototype method to have access to any instance's private data (like y).

Norguard
  • 26,167
  • 5
  • 41
  • 49
  • Thank you Norguard! I have some follow-up questions. What if there is some methods for `var point2D = function(x,y){....`? What are the differences of using `var point2D = function(x,y){....` over `function point2D(x, y){...`? And what are the advantages/disadvantages? – user1863421 Nov 30 '12 at 12:42
  • @user1863421 have a look at the edits -- hope this helps. Keep in mind that none of these solutions are one-size-fits all. Pick the one to meet the needs of the object you want a factory for at that time. Do they need private data/methods? Do they need lots of helper methods which could be shared across all instances (which need to access private data)? Or do you just need some simple objects which just have public state, and a few static methods which act on that public state? – Norguard Nov 30 '12 at 19:20
1

No, your first one was nearly correct:

function vector3d(x,y,z){
    vector2d.apply(this, arguments);
    // you might also use vector2d.apply(this, [].slice.call(arguments, 0,2));
    // or simpler vector2d.call(this, x, y);
    this.z=z;
}

So yes, you must call it from the constructor. Calling it once to create a prototype object would set up inheritance correctly, but create an instance of vector2D - which you neither need nor want. It might even cause harm. Have a look at the detailed answers on What is the reason [not] to use the 'new' keyword [for inheritance]?.

Is there a way of doing this with prototype?

vector3d.prototype = Object.create(vector2d.prototype);

will make the prototype object, from which all instances of vector3 inherit, inherit from the prototype object of vector2d. I'm not sure whether you need that, none of the usual twodimensional methods would apply on a threedimensional vector.

what is the advance of using prototypical inheritance?

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375