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!

Community
  • 1
  • 1
Manu Artero
  • 9,238
  • 6
  • 58
  • 73

1 Answers1

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.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 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 Artero Jun 09 '15 at 13:13
  • @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. – T.J. Crowder Jun 09 '15 at 13:18
  • Understood. Besides, In the second approach (the one with protos) The attribute ```capacity``` is public, NOW I'm reading the post you've post. – Manu Artero Jun 09 '15 at 13:20
  • 1
    @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.) – T.J. Crowder Jun 09 '15 at 13:26
  • Another qustion, Now if I create a box ```var o = new BOX_LOGIC.Box(334);``` the proto is **```BOX_LOGIC.Box.Box```** Why the second Box here? – Manu Artero Jun 09 '15 at 13:33
  • 1
    @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. – T.J. Crowder Jun 09 '15 at 13:45
  • 1
    FWIW; [here's a bit.ly link to babeljs.io/repl](http://bit.ly/1KZ7JHw) (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__`. – T.J. Crowder Jun 09 '15 at 13:46