DEV Community

Discussion on: Emulating "Private" Variables in JavaScript with Closures and Factory Functions

Collapse
 
ironydelerium profile image
ironydelerium • Edited

The one place where I never particularly liked this method personally is where the type of the object is also relevant - with your factory, each object is it's own thing, not using any sort of prototypical inheritance or the like.

For the times where I find it necessary to actually use truly private variables and still take advantage of JavaScript's built-in inheritance, there's also the WeakMap:

let NamedThing = (function() {
  const Private = new WeakMap();
  return class NamedThing {
    constructor(name) {
      // Yes, we're only storing one value in the "private object".
      // Of course, the example is a bit contrived.
      Private.set(this, { name });
    }

    // For comparison to the original. I'd use 'get name()' and 
    // 'set name(name)' instead, personally, but that I think is more
    // a matter of personal preference.
    getName() { return Private.get(this).name; }
    setName(name) { Private.get(this).name = name; }
  }
})();

let Animal = (function(NamedThing) {
  const Private = new WeakMap();
  return class Animal extends NamedThing {
    constructor(name, job) {
      super(name);
      Private.set(this, { job });
    }
    getJob() { return Private.get(this).job; }
    setJob(job) { Private.get(this).job = job; }

    // Just for exposition: this should always return undefined,
    // since NamedThing has it's own WeakMap, separate from this one
    getNameFromHere() { return Private.get(this).name; }
  }
})(NamedThing);

const presto = new Animal('Presto', 'Digger');
console.log(presto instanceof NamedThing); // true
console.log(presto.getName());             // Presto
console.log(presto.name);                  // undefined
console.log(presto.getNameFromHere())      // undefined
console.log(presto instanceof Animal);     // true
console.log(presto.getJob());              // Digger
presto.setJob('Bone Finder');
console.log(presto.getJob());              // Bone Finder
Enter fullscreen mode Exit fullscreen mode

Of course, if IE <11 support is something you care about, this isn't viable (even after being fed through Babel) unless leaking memory is OK with you.

Collapse
 
somedood profile image
Basti Ortiz

I like how clever this is. Surely, this has its uses, but for me, it's too much of a price to pay just to enable inheritance. It's one big soup of JavaScript obscurities (like how classes can be passed in as arguments to a function). I can imagine in my head how a traditional object-oriented developer would look at this and be like, "WHAT IS THIS??? JAVASCRIPT IS A HORRIBLE LANGUAGE!"

Collapse
 
ironydelerium profile image
ironydelerium

It depends on how important inheritance is for your use case. And while I haven't gone looking, I bet the Babel plugin for #name likely does much the same thing.