1

I need constructor that initialize some Objects with unique name. I write some code like this:

Obj.prototype.idCounter = 0;
Obj = function() {
   this.name = "Obj_" + Obj.prototype.idCounter;
   Obj.prototype.idCounter++;
}

var o1 = new Obj();
var o2 = new Obj();
alert(o1.name); // Obj_0
alert(o2.name); // Obj_1

But Obj.prototype.idCounter is non-private. I know how to create private variable, but I have no idea how to make private static variable.

8
  • 1
    Well the thing is... javascript can not really be mapped to a classic object oriented language, it is prototype oriented. So short answer: sorry, but you can't. Commented Mar 29, 2016 at 20:43
  • The introduction of the Symbol feature will make it possible to create code that's more robust against the threat of accidental property collisions, but it still will not be possible to truly hide object properties thanks to Object.getOwnPropertyNames() and Object.getOwnPropertySymbols(). For some purposes, you can use a closure. Commented Mar 29, 2016 at 20:46
  • Hi, just trying to learn here. Is a closure how you would create a private variable, and we're saying that making that private variable static is not possible in js? Commented Mar 29, 2016 at 20:47
  • @Observer if you wrap the constructor function itself in a closure (see my answer), then local variables in the closure are effectively private to all the functions created in that closure. In my answer, it's just the constructor, but other prototype functions could be created in the wrapper function too. Commented Mar 29, 2016 at 20:49
  • If I understand it right. Closures the only way to make encapsulation in JS Commented Mar 29, 2016 at 20:54

3 Answers 3

3

There is no way (as far as I know) to replicate private static members the way you described it.

What you could do to solve the problem of keeping your id generator safe is to use the module pattern, and use a sort of privileged access to it from within the module scope.

// using an immediately invoked function to generate the isolated scope
var Obj = (function () {
    var idCounter = 0;

    return function Obj() {
        this.name = "Obj_" + idCounter;
        idCounter++;
    };
}());

var o1 = new Obj();
var o2 = new Obj();
alert(o1.name); // Obj_0
alert(o2.name); // Obj_1
Sign up to request clarification or add additional context in comments.

Comments

3

What you can do is this:

var Obj = function() {
    var idCounter = 0;

    function Obj() {
       this.name = "Obj_" + idCounter;
       idCounter++;
    }
    return Obj;
}();

Now idCounter is a local variable to that anonymous function, so nothing else can see it except for the constructor function.

Note that you can also do some initialization of the prototype object in there too:

var Obj = function() {
    var idCounter = 0;

    function Obj() {
       this.name = "Obj_" + idCounter;
       idCounter++;
    }
    // one way to add a prototype property, and not
    // the simplest
    Object.defineProperties(Obj.prototype, {
      getInstanceCount: {
        value: function() {
          return idCounter;
        }
      }
    });
    return Obj;
}();

Now when you have an instance of Obj you can call getInstanceCount() to get a total number of objects created (which would be a weird way to design an API but it's just to illustrate that the prototype functions may get access to those private variables too).

5 Comments

@axelduch no don't - it's nice to have alternative versions.
Why do you use Object.defineProperties to assign a simple method?
@Bergi So glad you asked! :) I've been trying to get into the habit of defining prototype functions that way for a couple of reasons, mostly to make extending prototypes less weird but also to keep the own properties set (like via Object.keys()) clean. It's a personal affectation but the thing is that I really didn't think much about it when I typed in this code, so that means that the self-brainwashing I've been attempting has apparently worked.
I just find this additional {value: …} descriptor is cluttering the code. Object.assign is so much easier… and I really don't care about writability or enumerability of prototype properties. Is there a compelling reason to do?
@Bergi one person's compelling in another's pointless I guess :) I will add some commentary to the answer. edit oh also when I originally started typing in that edit to the answer I typed in a bunch of other random methods but I decided that was just too much stuff.
0

In JavaScript, you usually prefix field names with an underscore _ to indicate they are private. Of course this does not make them really private, but developers should show some self-discipline and consider it as private.

function Obj() {
  this.name = "Obj_" + (Obj._idCounter++);
}
Obj._idCounter = 0;

var o1 = new Obj();
var o2 = new Obj();
log(o1.name); // Obj_0
log(o2.name); // Obj_1

function log(x) { var p = document.createElement('pre'); p.appendChild(document.createTextNode(x)); document.body.appendChild(p); }

If you really want to hide something you can of course use closures, as others stated. However that is a bit harder to debug and for some reason that is a bit slower.

Comments

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.