0

Taking this list of similar questions:

  1. How to set up JavaScript namespace and classes properly
  2. Javascript namespace declaration with function-prototype
  3. Best OOP approach to these two small JavaScript classes

I'd concluded there are two possible ways for implementing classes and instances in JS: using an inner function or using a prototype.
So let's say we have a Box class inside the namespace BOX_LOGIC with a simple code in it. I'm able to code the following:

BOX_LOGIC.Box = (function() {
    // private static
    var boxCount = 0;

    var classDefinition = function(x) {
        x = x || 0;
        var capacity = x;
        var id = ++boxCount;

        // public methods
        this.getCapacity = function() { return capacity; };
        this.getId = function() { return id; };
        this.add = function(weight) { 
            weight = weight || 0;
            if (capacity >= weight) {
                capacity -= weight;
            }   
            return capacity;
        };
    };
    return classDefinition;
})();

As well as I'm able to code:

BOX_LOGIC.Box = (function () {
    var boxCount;

    var Box= function (x) {
        x = x || 0;
        this.capacity = x;
        this.id = ++boxCount;
    };

    Box.prototype = {
        Constructor: Box,
        add: function (weight) {
            weight = weight || 0;
            if (this.capacity >= weight) {
                this.capacity -= weight;
            }   
            return this.capacity;
        }
    };
    return Box;
})();

Mi question are: what is exactly the difference in using the Box prototype or not? is any approach better for any reason(cost, legibility, standard...)? Is in the second approach any way to emulate the static idvariable? THX!

2
  • 1
    @T.J.Crowder a typo, corrected Commented Jun 9, 2015 at 13:05
  • getCapacity is pretty useless in your second example. In your first one I'd prefer using a getter instead. It's worth noting that the two approaches are not mutually exclusive but can be combined. Commented Jun 9, 2015 at 13:23

1 Answer 1

1

Mi question are: what is exactly the difference in using the Box prototype or not?

is any approach better for any reason(cost, legibility, standard...)?

Functions (and other properties) on the prototype are shared between instances; if you create them in your constructor function, each instance gets its own copy of all of those functions.

The primary benefit is that you can add to the prototype, and even instances that already exist see the additions, since they use the prototype dynamically. In particular, this can help with aspect-oriented programming and various debugging and logging techniques, because you can dynamically wrap the functions on the prototype to capture calls. You can't do that when every instance has its own (unless you have a reference to every instance, which is unlikely).

In theory, using the prototype also means lower memory consumption. In practice, you'd have to have millions of instances to care, and modern engines are good at reusing the underlying code of the functions even though the function objects involved are distinct.

So unless you're going to augment the prototypes dynamically, one or the other is largely a matter of style.

Is in the second approach any way to emulate the static id variable?

I wouldn't have called it "static;" it's private to each instance. There are various ways to get close to private information with the prototype approach (that is, nearly-private information prototypical functions can access), but it's impossible to get truly get private information prototypical functions can access. There probably will be in ES7 (not ES6, which is currently being finalized; ES7). I address one of those near-private mechanisms in this blog post. The ES6 info in that post is now out of date; privacy stuff got pushed back to ES7, and "private Name" objects got morphed into Symbol which doesn't provide any real privacy at all.

I should flag up your third option, which you can use now with an ES6 transpiler: ES6's class:

// This is similar to the prototype version; `getCapacity` and
// `add` end up on `Box.prototype`
BOX_LOGIC.Box = (function () {

    class Box {
        constructor() {
            x = x || 0;
            this.capacity = x;
        }

        getCapacity() {
            return this.capacity;
        }

        add(weight) {
            weight = weight || 0;
            if (this.capacity >= weight) {
                this.capacity -= weight;
            }   
            return this.capacity;
        }
    }

    return Box;
})();

Side note: Since you used the word "correctly" in your title, I'll flag up a quite minor thing on your prototype example: You're messing up the constructor property. By default, Box.prototype has a property, constructor, that refers back to Box. But by replacing the object on Box.prototype with a completely different object, you're removing that. To be consistent with standard functions, I'd modify it like this:

Box.prototype = {
    constructor: Box,
    //...the existing stuff goes here...
};

Does it matter? Only if you end up with code that relies on the constructor property (and some libraries may). Nothing in JavaScript itself does, even though the JavaScript spec defines the property.

Sign up to request clarification or add additional context in comments.

7 Comments

I'm not able to test your third option. It's saying Uncaught SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
@Manu: Right, ES6 isn't finalized yet, and browser support is variable. Found that error message, you're using Chrome. Putting the code in strict mode would fix it (put "use strict"; at the beginning of the file, or just at the top of the anonymous function), but again, you can't rely on this without transpiling to ES5 (e.g., with Babel or similar) in the wild yet.
@Manu: In the prototype example's add, did you mean to use this.capacity? The example doesn't really make sense otherwise, probably don't need or want the var capacity there. (I've removed it and added this. to the class example.)
@Manu: The prototype is not BOX_LOGIC.Box.Box, but if you're looking in the console, it may be that the console is trying to give you as much context information as it can. The prototype of o will be an object, which like all objects can be referenced from any number of places. But in the code above, BOX_LOGIC.Box.Box is not one of them.
FWIW; here's a bit.ly link to babeljs.io/repl (the link is far too big for the comment box, as it has all the code in the URL) that demonstrates the class above and does a console.log on b.__proto__.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.