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?
Top comments (23)
Loved your article as it elucidates, in a very well-written way, the importance of closures for JS. I always use Apps Script to create stuff, and I have tested many approaches. Eventually, the chosen one was using classes. It's better for it gives you correct completions, allows you to properly add JSdoc @type and works fine in a GAS file environment. Personally, "free" variables, unattached to any object inside a function, makes me feel I'm dealing with a mess. I prefer attaching things to "this" and putting an underscore as prefix. Once I'm the only one using my programs, I have no security problems with private variables.
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:
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.
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!"
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.I wonder what the memory performance is like compared to this:
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?
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.
How funny is it, though, that this method is still using closures, the 5 functions capturing
privates
:DIt makes you realize that even though closures are so ubiquitous in JavaScript, not everyone truly understands it.
1.
Uncaught SyntaxError: missing ) after argument list
You would have caught this one if you used highlighting:
vs
2.
Uncaught SyntaxError: Identifier 'privates' has already been declared
(pointing at the function, not theconst
)Have you forgotten declaration collision? (TypeScript would also have caught it statically)
3. "globals" are fine when you are thinking in modules
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
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.
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 :/
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.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?
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.
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
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.
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 creates
OBJECTS
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...
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.
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.
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. π
Ooh. Now this is interesting.