Private in JavaScript ES5 (Part 1)

Did you know that private, protected and public are reserved words in JavaScript?
Really.. I mean it!!

Take a look at this MDN page – Reserved Identifiers.
Oh Wow! It means I can create private, protected fields and methods in JavaScript? Sadly NO!
These have been reserved since ages but JavaScript hasn’t used them.

Oh! So how do I create private field in JavaScript?
The answer is – It Depends!

Depends on what?
Depends on which version of ECMAScript you are writing code in.

Let's first understand why everything is public in JavaScript

JavaScript does NOT have classes (yea, yea.. that class keyword is just sugar coating)
JavaScript is a “prototype” object oriented language, which means there are ONLY objects.

Watch this DigitalCV video to find out How JavaScript Dictionaries look like

JavaScript Process Map and Dictionaries


And guess what these objects are? DICTIONARIES! That too with all members being visible, though you can change some properties to be non-enumerable, but it doesn’t really hide them.

So how do we hide properties of an object?

Even Object is made up of properties (keys in its dictionary)
Every property is again a dictionary (called Descriptors)

There are 4 descriptors

DescriptorDescription
valuevalue of the property
writable if false, the property is readonly
enumerableif false, the property does not show up in for..in or console.log
configurableif false, writable and enumerable can no longer be changed
Property Descriptors
Let's try hiding by property descriptor
/*
 * We are using old style for creating objects
 * Actually the "true" style as there is no class in JS
 */
function Person(id, name) {

    // Property descriptor for id property
    const idPropertyDescriptor = {
        value: id,
        writable: true,
        enumerable: false
    };
    Object.defineProperty(this, 
        "id", 
        idPropertyDescriptor
    );
    
    // Property descriptor for name property
    const namePropertyDescriptor = {
        value: name,
        writable: false,
        enumerable: true
    };
    Object.defineProperty(this, 
        "name", 
         namePropertyDescriptor
    );
}

// Let's create an object of type Person
let eich = new Person(1, "Brendan");

// id is enumerable false, hence won't show up
console.log('Printing entire object');
console.table(eich);

// But id exists and CAN be changed and printed
eich.id = 7;

// However, name is read-only... wont change
console.log('Trying to change name to Eich')
eich.name = "Eich";

// So id is not really private, just hidden
console.log('Printing properties by name')
console.log(`id=${eich.id}, name=${eich.name}`);

And.. here is the output!
Printing entire object
┌─────────┬───────────┐
│ (index) │  Values   │
├─────────┼───────────┤
│  name   │ 'Brendan' │
└─────────┴───────────┘
Trying to change name to Eich

Printing properties by name
id=7, name=Brendan

Nope! Enumeration does not really make the property private, it merely “tries” to hide it, and that too is not very successfully because if you know the name, you can not only print it but even modify it.

We failed! 😔

Closures to the rescue

Huh? What is a closure?
When a function can “capture” (or close over) variables from outer functions, it creates a “closure” scope.

/*
 * Function is a way of creating objects in JS
 * Think of Account() as cosntructor
 */
function Account(id, name, bal) {

    // properties of the object
    this.id = id;
    this.name = name;

    // This is NOT a property
    // Local variable to Account
    let balance = bal;

    this.deposit = function(amount) {
        // deposit "closes over" balance 
        balance += amount;
    }

    this.withdraw = function(amount) {
        // balance is NOT a property
        // but local variable of outer function
        balance -= amount;
    }

    this.print = function print() {
        console.log(`ID=${this.id}, 
                     Name=${this.name}, 
                     Balance=${balance}`);
    }
};

Now lets runs this code
var eich = new Account(1, "Brendan", 10000.00);
eich.print();
eich.deposit(1000);
eich.print();
eich.withdraw(500);
eich.print();
And here is the output
ID=1, Name=Brendan, Balance=10000
ID=1, Name=Brendan, Balance=11000
ID=1, Name=Brendan, Balance=10500

Wait a minute… balance is behaving just like a field. We can update it, print it and YET it’s NOT a property of the object. So where is it?

The answer is – “It’s NOT inside the object dictionary but has been put in a different dictionary called “closure”, which is available to deposit and withdraw functions when they are called.

Sweet! So it it hidden from us? Like a private field? YES!

console.log(eich);
Account {
  id: 1,
  name: 'Brendan',
  deposit: [Function (anonymous)],
  withdraw: [Function (anonymous)],
  print: [Function: print]
}

YESS!! We have managed to “hide” balance and made it a “private” field of Account class.
But wait a minute.. what if we directly access it, like…

console.log(eich.id, eich.name, eich.balance);

Out of luck! It’s NOT inside the object, so we CANNOT access it at all. Only when the functions deposit and withdraw are called, they can “see” it in the closure dictionary

1 Brendan undefined
So, is this the way we create private methods too?

Absolutely! Just like local variables of outer function, even local functions are “captured” by closures.

Creating "private" functions
function Account(id, name, bal) {

    // properties of the object
    this.id = id;
    this.name = name;

    // Local variable to Account
    let balance = bal;

    // NOT part of the object
    // Local function (like private)
    // Cannot be called from outside
    function sendSMS() {
        console.log(`Sent SMS: Updated balance={balance}`);
    }

    this.deposit = function (amount) {
        // deposit "closes over" balance 
        balance += amount;

        // Can call local function
        sendSMS();
    }

    this.withdraw = function(amount) {
        // balance is NOT a property
        // but local variable of outer function
        balance -= amount;

        // Can call local function
        sendSMS();
    }

    this.print = function print() {
        console.log(`ID=${this.id}, 
                     Name=${this.name},
                     Balance=${balance}`);
    }    
};
Let's call it from outside
let eich = new Account(1, "Brendan", 10000.00);
eich.print();
eich.deposit(1000);
eich.print();
eich.withdraw(500);
eich.print();

// Try to sendSMS from outside the object
eich.sendSMS();

Nope! Does not work
ID=1, Name=Brendan, Balance=10000
Sent SMS: Updated balance=11000
ID=1, Name=Brendan, Balance=11000
Sent SMS: Updated balance=10500
ID=1, Name=Brendan, Balance=10500

eich.sendSMS();
     ^
TypeError: eich.sendSMS is not a function

Wonderful! We have managed to create private fields and functions using ES5 syntax!

Wait… what about ES6? class syntax?
Wait for the next blog post 🙂

Private fields in JavaScript Series

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s