JavaScript ES6 brought a lot of changes like const, let, default arguments, spread operator, classes..
Wait WHAT? JavaScript now is a “class” based OO instead of “prototype” based OO???
Relax! class
is just syntactical sugar coating and it’s still “prototype” based OO language
I repeat.. internally JS is NOT class based OO language
Hey, but there is a class keyword! Yes, and it still creates objects in memory.

Let’s compare ES5 “class” with ES6 class
How to create a "class" in ES5
// The following is NOT ES6 class keyword
// But if we "imagine" a class
// function looks like a constructor 🙂
// class Account
// {
function Account(id, name, bal) {
// Public "fields"
this.id = id;
this.name = name;
// Private "field"
// NOT stored in the object
// Stored in a "closure"dictionary
// Which is visible ONLY inside this scope
let balance = bal;
// Operations
this.deposit = function (amount) {
// Inner functions can access
// closure variables like balance
balance += amount;
}
this.withdraw = function (amount) {
// balance is NOT a field
// But we can access outer variables
balance -= amount;
}
this.print = function () {
console.log(
`ID: ${this.id}`,
`Name: ${this.name}`,
`Balance: ${balance}`
)
}
}
// }
let eich = new Account(1, "Brendan", 10000);
eich.print();
And here is the output
ID: 1 Name: Brendan Balance: 10000
So how do we create class in ES6?
I repeat.. internally JS is NOT class based OO language
/*
* ES6 has a class keyword
* But internally there are no classes
*/
// Using ES6 syntax
class Account {
// Instead of function Account(...)
constructor(id, name) {
// Public "fields"
this.id = id;
this.name = name;
}
// Member function
print() {
console.log(
`ID: ${this.id}`,
`Name: ${this.name}`,
)
}
}
let eich = new Account(1, "Brendan");
eich.print();
Here is the output
ID: 1 Name: Brendan
Now let’s add a “private” field called balance in our Account class. Hmm.. where?
In our ES6, we just defined a closure variable in the outer function Account and that was available to inner functions like deposit and withdraw. So let’s try that
/*
* ES6 has a class keyword
* But internally there are no classes
*/
// Using ES6 syntax
class Account {
balance = 10000;
// Instead of function Account(...)
constructor(id, name) {
// Public "fields"
this.id = id;
this.name = name;
}
// Member function
print() {
console.log(
`ID: ${this.id}`,
`Name: ${this.name}`,
`Balance: ${balance}`
)
}
}
let eich = new Account(1, "Brendan");
eich.print();
And here is the output
`Balance: ${balance}`
^
ReferenceError: balance is not defined
Huh? What just happened? Isn’t balance a “closure” variables like ES5? NO!
class is NOT a function and variables inside are not local variables. In fact, balance=10000
has become a class level static field. It’s NOT in some closure.
Oh ok, SO? Even if it’s part of the class object, should I be able to acccess it? Yes and No.
class object itself is not part of eich object’s prototype chain (topic for another blog?), so it wont show up. Even if it did, there would be just one balance field for ALL objects. Hey, it would be nice to share such Account implementation with some billionaire – YOUR balance is now MINE! 🙂
Ok ok, so how do we create “private” variables in ES6? Unfortunately, there is no direct way!
But we can store data in closure variables, just like ES5, though it would have to be outside the class.
/*
* ES6 has a class keyword
* But internally there are no classes
*/
// Local variable to module
let balance = 0;
// Using ES6 syntax
class Account {
// Instead of function Account(...)
constructor(id, name, bal) {
// Public "fields"
this.id = id;
this.name = name;
// Closure variable
balance = bal;
}
// Member function
print() {
console.log(
`ID: ${this.id}`,
`Name: ${this.name}`,
`Balance: ${balance}`
)
}
}
let eich = new Account(1, "Brendan", 10000);
eich.print(); // Should print 10000
let ray = new Account(2, "Romano", 20000);
ray.print(); // Should print 20000
eich.print(); // ?
And here is the output
ID: 1 Name: Brendan Balance: 10000
ID: 2 Name: Romano Balance: 20000
ID: 1 Name: Brendan Balance: 20000
Oops.. eich seems to be using ray’s balance!
Well, that’s because there is only ONE closure variable called balance, so whoever sets it last.. wins!
Fixing it.. with Map!!
/*
* ES6 has a class keyword
* But internally there are no classes
*/
// Local variable to module
let balances = new Map();
// Using ES6 syntax
class Account {
// Instead of function Account(...)
constructor(id, name, bal) {
// Public "fields"
this.id = id;
this.name = name;
// Closure map
// each object (this) has its own balance
balances.set(this, bal);
}
// Member function
print() {
console.log(
`ID: ${this.id}`,
`Name: ${this.name}`,
`Balance: ${balances.get(this)}`
)
}
}
let eich = new Account(1, "Brendan", 10000);
eich.print(); // Should print 10000
let ray = new Account(2, "Romano", 20000);
ray.print(); // Should print 20000
eich.print(); // Should print 10000
ID: 1 Name: Brendan Balance: 10000
ID: 2 Name: Romano Balance: 20000
ID: 1 Name: Brendan Balance: 10000
There is however ONE problem… balances map will live on forever (technically until the process end). That’s because even after eich and ray objects are long gone, balances map keeps waiting that someday they will return and ask for their balance, but they are dead and long gone!
Hmm.. so what do we do? How can we ensure that if an object dies, it’s balance is also released?
WeakMap!!!
A WeakMap is a Map which does hold reference to other objects, but when it realises that it’s the only one holding on to that reference, it releases it. Problem SOLVED!
/*
* ES6 has a class keyword
* But internally there are no classes
*/
// Local variable to module
// WeakMap releases reference
// If it's the only one holding it
let balances = new WeakMap();
// Using ES6 syntax
class Account {
// Instead of function Account(...)
constructor(id, name, bal) {
// Public "fields"
this.id = id;
this.name = name;
// Closure map
balances.set(this, bal);
}
// Member function
print() {
console.log(
`ID: ${this.id}`,
`Name: ${this.name}`,
`Balance: ${balances.get(this)}`
)
}
}
let eich = new Account(1, "Brendan", 10000);
eich.print(); // Should print 10000
let ray = new Account(2, "Romano", 20000);
ray.print(); // Should print 20000
eich.print(); // Should print 10000
There! It’s done. We have implemented private fields in JavaScript ES6 with help of WeakMaps. Callers cannot access balance, as balance is not a “field” inside the object (ALL object properties are accessible, yes, even if you create them as Symbol
, but balance being a closure variable, it’s accessible only within the class which has captured it.
Complete code with all fields as private
/*
* ES6 has a class keyword
* But internally there are no classes
*/
// Local variable to module
// WeakMap releases reference
// If it's the only one holding it
let _private = new WeakMap();
// Using ES6 syntax
class Account {
// Instead of function Account(...)
constructor(id, name, balance) {
// Let's create an internal object
let _fields = {
_id: id,
_name: name,
_balance: balance
};
// Now add it to closure variable
_private.set(this, _fields);
}
// Nice lil touch... getters
get id() {
return _private.get(this)._id;
};
get name() {
return _private.get(this)._name;
};
get balance() {
return _private.get(this)._balance;
}
set balance(value) {
throw 'balance is private';
}
// Account operations
withdraw(amount) {
// Modify our private field
_private.get(this)._balance -= amount;
}
deposit(amount) {
// _balance is in closure WeakMap
// Not accessible from outside
_private.get(this)._balance += amount;
}
// Member function
print() {
console.log(
`ID: ${this.id}`,
`Name: ${this.name}`,
`Balance: ${this.balance}`
)
}
}
let eich = new Account(1, "Brendan", 10000);
eich.print(); // Should print 10000
let ray = new Account(2, "Romano", 20000);
ray.print(); // Should print 20000
// We can "access" balance thru getters
console.log(eich.id, eich.name, eich.balance);
// Cannot set balance though
eich.balance = 50000;
console.log(eich.balance);
And here is the output
ID: 1 Name: Brendan Balance: 10000
ID: 2 Name: Romano Balance: 20000
1 Brendan 10000
throw 'balance is private';
^
balance is private
Yay! We have created “private” fields in ES6 class, but how about private functions?
Well, we can store functions also in the _fields object, but that would be wasteful because it will store a copy of the function in EACH object. The SAME function in EVERY object?
There has to be a more efficient way… If you have ideas about that, write them in comment