loading...
Cover image for Emulating "Private" Variables in JavaScript with Closures and Factory Functions

Emulating "Private" Variables in JavaScript with Closures and Factory Functions

somedood profile image Basti Ortiz (Some Dood) ・5 min read

Despite the recent implementation of classes in JavaScript, there has never been a native way of controlling the visibility of an object's property. Specifically, there has never been a way to actually make variables private. For now, workarounds are our best bets. One of the most common workarounds is the underscore notation. It is simply the convention of prepending an underscore (_) to a variable name. This is done to indicate that a variable is private and should not be toyed with. For example, a "private" variable that stores sensitive information, such as a password, will be named _password to explicitly state that it is "private". However, it can still be accessed and mutated by writing someObj._password. It is just like any other object property that you can alter. The underscore is merely a symbol prepended to some identifier. Frankly, the prepended underscore is just there by convention as an unenforced deterrent to those who may have ideas to access and mutate the "private" variable.

What is a private variable?

In many object-oriented programming languages, there is a way to limit the visibility of a variable from outside its scope. In other words, some programming languages allow variables to only be accessible by the object that "owns" it. To be more technical, a private variable is only visible to the current class. It is not accessible in the global scope or to any of its subclasses. For example, we can do this in Java (and most other programming languages) by using the private keyword when we declare a variable. Attempting to access the private variable outside of the class that owns it will throw an error.

// Example Class
class Example {
    // hiddenVariable CAN only be accessed here
    private String hiddenVariable;

    public Example(String websiteName) {
        hiddenVariable = websiteName;
    }
}

// Main Method
public class Main {
    public static void main(String[] args) {
        // Instantiate class
        Example website = new Example("DEV.to");

        // This will throw an error
        // error: hiddenVariable has private access in Example
        System.out.println(website.hiddenVariable);
    }
}

Making variables private is done for many reasons ranging from security to encapsulation. In this case, private variables can only be indirectly accessed and manipulated using good ol' getter and setter methods.

Closures

In JavaScript, when a function finishes executing, any variables declared within its body is "garbage collected". In other words, it is deleted from memory. This is why local variables are possible in JavaScript. This is why variables inside functions cannot be accessed outside.

// dev is NOT accessible here
function someFunc() {
  // dev is accessible here
  const dev = 'to';
}
// dev is NOT accessible here

Special exceptions occur when something inside the function depends on the variable being deleted. For example, the function below returns another function that depends on the variables of the parent function.

// Parent function
function parent() {
  // Local variable of the parent function
  const prefix = 'I am a ';

  // Child function
  return function(noun) {
    // The child function depends on the variables of the parent function.
    return prefix + noun;
  };
}

NOTE: The example above takes advantage of a concept in functional programming called currying. You can read more about it if you want.

// Store the returned child function
const getSentence = parent();

// At this point, `parent()` has finished executing.
// Despite that, the `prefix` variable is still
// accessible to the child function. More on that later.
const job = getSentence('programmer');

// What is the value of `job`?
console.log(job); // 'I am a programmer'

In this case, prefix is still usable by the child function even after it has been garbage collected because the child function created its own closure. A closure is like a "snapshot" of the environment a function is in when it is executed. Its closure is its own internal copy of the environment.

Technically speaking, any variable in a closure is exclusively accessible to the child function that owns it. Operations can only be performed on these variables if the current execution context has a reference to the closure. In this case, the "snapshot" that the child function owns is the reference to that closure, therefore it has access to its variables.

When the parent function finished executing, the prefix variable is scheduled to be deleted. However, before that can be done, the child function "takes a snapshot" of its current environment (which includes all of the variables of the parent function it depends on). The child function now has its own copy of the prefix variable that it can access and manipulate. This is what closures are in its most basic use case. MDN provides a more technical definition.

Factory Functions

A factory function is any function that returns an object. Yup, that's pretty much it. This is not to be confused with classes and constructor functions. Classes and constructor functions require the new keyword to instantiate objects while factory functions return the instantiated object itself.

function factory(name) {
  return { name };
}

const obj = factory('Some Dood');
console.log(obj.name); // 'Some Dood'

Using Closures for Private Variables

We now have all the knowledge needed to emulate "private" variables in JavaScript. We can begin by writing a factory function that returns an object with getter and setter methods. The factory function takes in two arguments that correspond to the "private" properties of the returned object.

function createAnimal(name, job) {
  // "Private" variables here
  let _name = name;
  let _job = job;

  // Public variables here
  return {
    // Getter Methods
    getName() {
      return _name;
    },
    getJob() {
      return _job;
    },
    // Setter Methods
    setName(newName) {
      _name = newName;
    },
    setJob(newJob) {
      _job = newJob;
    }
  };
}

We can then invoke the factory function to create new instances of an animal object. Note that every time we invoke the factory function, a new closure is created. Therefore, each returned object has access to its own closure.

const presto = createAnimal('Presto', 'Digger');
const fluffykins = createAnimal('Fluffykins', 'Jumper');

So what have we achieved by doing this? Well, with the power of closures, we have essentially emulated "private" variables in JavaScript.

// These properties will be inaccessible
console.log(presto._name); // undefined
console.log(presto._job); // undefined
console.log(fluffykins._name); // undefined
console.log(fluffykins._job); // undefined

// Getter methods have access to the closure
console.log(presto.getName()); // 'Presto'
console.log(presto.getJob()); // 'Digger'
console.log(fluffykins.getName()); // 'Fluffykins'
console.log(fluffykins.getJob()); // 'Jumper'

// Setter methods can mutate the variables in the closure
presto.setName('Quick');
presto.setJob('Bone Finder');
fluffykins.setName('Mittens');
fluffykins.setJob('Fish Eater');

console.log(presto.getName()); // 'Quick'
console.log(presto.getJob()); // 'Bone Finder'
console.log(fluffykins.getName()); // 'Mittens'
console.log(fluffykins.getJob()); // 'Fish Eater'

A Strange Concoction of Programming Paradigms

This workaround is indeed a strange way to achieve a seemingly simple feature of object-oriented languages. But if one were to analyze this very closely, there is beauty in this workaround. For one, it cohesively glues together two different and rather conflicting programming paradigms: object-oriented and functional programming.

The object-oriented nature of this approach involves the use of factory functions, mutability, and encapsulation. On the other hand, the functional approach involves the use of closures. JavaScript truly is a multi-paradigm language that continues to blur the borders between the contrasting paradigms.

One could argue that gluing the two paradigms together is messy and peculiar. In my opinion, I wouldn't say that that's entirely correct. Even if the amalgamation of paradigms does not follow conventions and design patterns, I find it greatly fascinating that to implement an object-oriented feature in JavaScript, one must use the features of functional programming. The two contradicting paradigms work together in harmony, similar to that of the yin and yang. Despite their differences, there is always a way to make things work. Perhaps this could be an analogy for life?

Discussion

pic
Editor guide
Collapse
ironydelerium profile image
ironydelerium

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

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 (Some Dood) Author

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.

Collapse
qm3ster profile image
Mihail Malo

I wonder what the memory performance is like compared to this:

const privates = new WeakMap()
class Animal {
  constructor(name, job) {
    privates.set(this, { name, job })
    this.aPublicOne = true
  }

  // Real Getters
  get name() {
    return privates.get(this).name
  }
  get job() {
    return privates.get(this).job
  }
  // Real Setters
  set name(name) {
    privates.get(this).name = name
  }
  set job(job) {
    privates.get(this).job = job
  }
}

Liek, I know instantiating closures isn't the same as actually declaring separate different functions. But it also is more expensive than object methods, to my great disdain.
Will this be somewhere in the middle, or actually worse than both?

Collapse
somedood profile image
Basti Ortiz (Some Dood) Author

I'm not sure myself, honestly. I'd love to know. When I have the time, I'll try to create a rigorous experiment for this using the DevTools Memory tools.

If I were to make an educated guess, I'd say the difference in memory efficiency would be negligible.

Collapse
qm3ster profile image
Mihail Malo

How funny is it, though, that this method is still using closures, the 5 functions capturing privates :D

Thread Thread
somedood profile image
Basti Ortiz (Some Dood) Author

It makes you realize that even though closures are so ubiquitous in JavaScript, not everyone truly understands it.

Collapse
swarupkm profile image
Swarup Kumar Mahapatra

I have been practising Factory Functions for a while in this repo
github.com/swarupkm/practise-js/tr...

The same I have done in Java as typical Object Oriented.

In this link medium.freecodecamp.org/class-vs-f... I found this quotations

I think Class Free Object Oriented Programming is JavaScript’s gift to humanity.
— Douglas Crockford “The Better Parts”

Collapse
somedood profile image
Basti Ortiz (Some Dood) Author

Douglas Crockford truly is the legend of JavaScript. I totally agree with that quote.

I bet I would go insane if I needed to write a class for every time I needed to instantiate objects. Just imagine the hassle it would be to pass in an object as an argument to a function if classes were required to instantiate that object.

// Method #1 and Method #2 achieve the exact same thing.

// METHOD #1
class Options {
  constructor(value, isWritable) {
    this.value = value;
    this.writable = isWritable;
  }
}

Object.defineProperty(window, 'dev', new Options('to', false));

// METHOD #2
Object.defineProperty(window, 'dev', {
  value: 'to',
  writable: false
});
Collapse
qm3ster profile image
Mihail Malo

That's actually similar to how options work in Rust.
You construct a struct.
If it's big, you often use a builder to not have to construct it all at once.
However, it all works out great in the end, and everyone is ecstatic :/

Collapse
swarupkm profile image
Swarup Kumar Mahapatra

Nice article. Can I guess that you have been influenced by youtube.com/watch?v=ImwrezYhw4w ?

I have been influenced for sure, and I deeply appreciate the multi-paradigm possibility in JavaScript

Collapse
somedood profile image
Basti Ortiz (Some Dood) Author

Ooooooh! I am so glad to find a fellow fan of Fun Fun Function here. Unfortunately, no, I was not entirely influenced by the video. "Entirely". I did learn about the concept from that video, though.

The inspiration behind this article comes from a thought experiment I had while I was in my shower. I just wondered how to implement private variables in JavaScript and POOF! I made an article about it.

Collapse
swarupkm profile image
Swarup Kumar Mahapatra

Yup, I am a big fan of Fun Fun Functions. Fluffykins is the word I keep hearing about in his videos , hence I thought you might know about it.

Factory Functions . I have recently started working on nodejs and previously I had background in Java, Python , Ruby and I followed traditional OOPS programming.

However by going through FFF videos, I am convinced to think functionally and create Objects that way. Ultimate goal is to createsOBJECTS whatever way it may be.

Implementation of Classes in JS feels like implementation of Functional Programming in Java. Basically they are retrofitting stuff.

This particular rant against classes by FFF was awesome. youtube.com/watch?v=Tllw4EPhLiQ&li...

Thread Thread
somedood profile image
Basti Ortiz (Some Dood) Author

Yes! Someone finally caught on with the Fluffykins reference. I've been using it in my code examples recently in my recent posts in the hopes of finding a fan of the show. You, my friend, are the first that I have found.

That is why I find JavaScript to be one of the best languages out there. It just finds a way to combine various paradigms, which makes you think differently about programming. Traditional programming makes you strictly think in a specific way. For me, it doesn't open my mind to new possibilities enough, which is why I grew to love JavaScript.

Collapse
anodynos profile image
Angelos Pikoulas

Has anyone tried something as naive as this?

The 101 smoke test worked, it seems like _flees are gonna be garbage collected, what could go wrong? You can also attach getters and setters and improve it quite more, make it readonly or writeonly, etc.

class Dog {
  constructor() {
    let _flees = 0;
    this.getFlees = () => _flees;
    this.setFlees = (f) => {_flees = f};
  }
}

const a = new Dog;
const b = new Dog;

a.setFlees(1);
b.setFlees(2);
console.log(a.getFlees());
console.log(b.getFlees());

a.setFlees(11);
b.setFlees(22);
console.log(a.getFlees());
console.log(b.getFlees());
// output 1 2 11 22

The obvious side effect of this simplicity is of that _flees won't be able to be accessed by other methods at all, they all have to use the accessor methods.

So they are super-private.

But is this a wrong thing necessarily? I'm only entertaining the idea, but unless one uses it in practice, one can't know really...

Any thoughts?

Collapse
somedood profile image
Basti Ortiz (Some Dood) Author

Honestly, I doubt that anyone has the time to add this level of complexity just for privacy and encapsulation in JavaScript. I wrote this post to inform people that it is indeed possible, albeit rather impractical. Most of us would just prefer using the _ naming convention to denote member privacy.

I wouldn't say it's a "wrong thing", per se. It's still a cool party trick and all, but if one hopes to use this in production code, they better hope that the next person maintaining their code knows enough about JavaScript to understand why all the boilerplate code is necessary for member privacy.

Collapse
Sloan, the sloth mascot
Comment deleted
Collapse
jreina profile image
Johnny Reina

Prepending private variables with an underscore is a convention that has been used by c/c++ developers since the beginning of time. I still do it to the same effect but it is becoming less common with newer devs entering the workforce who may not have been exposed to this convention.

Collapse
theycallmenucci profile image
Steve Antonucci

Interesting. Thank you for the insight.

As an older dev who has long grown past his C/C++ zenith, I can't say it was a convention I was ever exposed to in the workforce myself; surprisingly. Can't say I ever really bothered to look it up either. 😖 Sounds like I need to get out more. 😀

Collapse
citizen428 profile image
Michael Kohl

Despite the recent implementation of classes in JavaScript, there has never been a native way of controlling the visibility of an object's property.

Soon: Stage 3 proposal for private fields, available already in Babel.

Collapse
somedood profile image
Basti Ortiz (Some Dood) Author

Ooh. Now this is interesting.