Privatizing Javascript Variables

Safely Avoiding the Global Scope

Joshua Coquelle
5 min readApr 18, 2016

Unlike languages such as Java or PHP, Javascript does not contain a private keyword for declaring variable/function accessibility. As an example, here is how we could create a Dog class in PHP containing a private variable and then we may access it via a public ‘getter’ method.

<?phpclass Dog
{
private $species = 'Dog';

public function getSpecies ()
{
return $this->species;
}
}
$dog = new Dog;

Now that we have our new Dog class instance named $dog, we can try to access the private $species variables on the instance.

// Attempt to access the class property $speciesecho $dog->species;Fatal error: Uncaught Error: Cannot access private property Dog::$species

PHP throws us an error due to the privacy constraints of the variable that we set, which is why we have to use a public ‘getter’ method to access our private variable. (This is overall just good Object Oriented practice).

// Returns us the variable value 'Dog'echo $dog->getSpecies();

Since this isn’t an article on Object Oriented PHP, let’s get into a similar example with Javascript and find a way to emulate the same type of privacy scope for our variables.

Let’s pretend for a moment that we have a javascript file called dog.js where we will create two dog objects using prototypal inheritance rather than classical OOP inheritance (A topic for a different article but just play along).

// dog.jsvar Dog = {
species: 'Dog',
getSpecies: function () {
return this.species;
}
};
var Rocket = Object.create(Dog);
var Buddy = Object.create(Dog);

Although the example is contrived, let’s assume we are just fine with the Dog objects default properties and don’t need a constructor. Now that we have our Dog object literal and have created two copies of it, let’s log some properties out to the console.

// Logs 'Dog' to the consoleconsole.log(Rocket.species);
console.log(Rocket.getSpecies());
// Logs 'Dog' to the consoleconsole.log(Buddy.species);
console.log(Buddy.getSpecies());

This seems all fine and dandy, however now let’s pretend we have someone who is using our Dog object but decides that they are more of a cat person, so they go into the console and type:

Dog.species = 'Cat';

Oops…

Due to our Dog object being completely public as well as all of its properties, anyone using the object can alter the properties, in return screwing up our hard work! Now take a look at what we have:

Rocket.species;
Rocket.getSpecies();
Buddy.species;
Buddy.getSpecies();
// 'Cat' is logged to the console, although these are Dog objects!!

NOTE: If you are wondering why changing the Dog object has a direct impact on the Rocket/Buddy objects, it is because the properties on our Rocket/Buddy objects are pointing to the Dog prototype which contains the actual value, therefore mutating the Dog object changes all of the ‘copies’ that point to it.

We need to stop this from happening, we cannot have our dogs be turned into cats! (Cats will kill you in your sleep). For our own safety, we must prevent this behaviour. The question that remains is… how the hell do we do this!?!?

Functions!

Because of the way that Javascript handles variable scope, you always have access to variables that have been defined in the parent scope of your function. As an example, you will see that we have a favouriteMovie() function that contains a variable called title.

Since we have declared this variable inside of our favouriteMovie() function, it creates a CLOSURE scope, meaning we have our variable enclosed and protected to outside mutation. As you can see, we also have a logMovie() function that has access to and logs out the title variable, this is because the title variable is in the parent scope of logMovie() which allows it to be seen by logMovie().

function favouriteMovie () {
var title = 'The Dark Knight';
function logMovie () {
console.log(title);
}
logMovie();
}

Think of this scope concept as being at the bottom of a staircase. Looking up the stairs you can see each step (the variables) that get higher and higher until the top (Window object), however if you look down you see nothing but the ground you’re standing on (cannot see variables below your scope).

Window object - // Top of stairs
-------------
| favouriteMovie() - // I see you Window object
------------------
| logMovie() - // I see you favMovie
------------
|
-------------------------------------------

This concept can be tough for beginners to grasp, (especially because I just made up this analogy and it may make zero sense to you), but if you play around in your Chrome console and try nesting functions within each other and accessing their outer/parent variables, you will understand it in no time!

Coming back to the Dog object

Let’s wrap up by fixing our privacy issues before we have our objects turned into more cats! There are a few different ways of doing this, especially if you take a more classical OOP inheritance approach by using the new keyword to create your objects, however since this has been known to have it’s pitfalls because Javascript is a Prototypal language and not a true OOP language, we will use a factory function simulate our privacy.

Think of a factory function as something you can call that just spits out new objects, kind of like an assembly line in a factory.

Here we have our original Dog object:

var Dog = {
species: ‘Dog’,
getSpecies: function () {
return this.species;
}
};

Let’s throw this into a factory function, which will ONLY expose our function name DogFactory() to the global scope.

// Our global variable for entry
var DogFactory = function () {
// Blueprint Dog object
var Dog = {
species: ‘Dog’,
getSpecies: function () {
return this.species;
}
};
// Return our Dog object copy
return Object.create(Dog);
};

Now that we have a working factory function, we can create new Dog objects whenever we want and no one will be able to modify the properties or turn our dogs into cute (yet evil) cats.

var Rocket = DogFactory();
var Buddy = DogFactory();
console.log(Rocket.species);
console.log(Rocket.getSpecies());
console.log(Buddy.species);
console.log(Buddy.getSpecies());
// All dog variables log out 'dog' as it's species as expected

Since we have encapsulated our variables in the factory function, trying to access and change the species property of the default Dog object now becomes not possible.

Dog.species = 'Cat';
Uncaught ReferenceError: Dog is not defined
DogFactory.Dog.species = 'Cat';
Uncaught TypeError: Cannot read property 'species' of undefined

There you have it! We have privatized our variables inside of a function and are now able to safely create multiple Dog objects on the fly without the worry of having our data altered. Although ES6 has taken care of quite a few of these issues, it is still good to know how to control your own privacy without ES6.

There are many design patterns that can control privacy by the way you structure and expose your code, my favourite being the Revealing Module Pattern which I will cover in another article.

See you space cowboy…

--

--

Joshua Coquelle

Developer. Alpaca Aficionado. Corgi owner. Technical blogging for beginner to intermediate Javascript developers.