Wednesday, 7 January 2015

Private Members in ES6 Classes

Classes in ECMAScript 6 are easy to write and use. Yet, they have certain limitations. One of them is, there is no direct way to create a private member inside the class.

The classes don’t allow us to declare variables directly inside body of the class. All members defined/declared inside the class or attached to an instance using this keyword are directly available to the instance of the class.

When I Googled it, I found a few posts on it and answers suggesting to use Symbol. I think, Symbol was private sometime back but according to the current draft of ES6, properties created using Symbol values are not private anymore. Though value of Symbol can't be reproducible, one can read Symbol properties using Object.getOwnPropertySymbols.

For example, consider the following class and object:


const ID = Symbol();
class Employee{
  constructor(id, name){
    this.name = name;
    this[ID] = id;
  }
}
var emp = new Employee(1001, "Ravi");


Though value of id is not accessible using emp[Symbol()], as it returns a different Symbol, we can extract all Symbols used for properties and values of these properties as:

var symbolsInEmp = Object.getOwnPropertySymbols(emp);
symbolsInEmp.forEach(function(symbol){
  console.log(emp[symbol]);
});


The above snippet will print value of id stored in the object emp. If there are multiple Symbol properties, you may not be able to find which value corresponds to what value, but the data is not private.

To make a value private, we can do one of the following:

  • set and use the value inside a closure
  • create the variable inside a module and not export it


WeakMap seems to be a good option for creating the private fields. Because, it accepts any value as key and it doesn’t prevent the key object from getting garbage collected when the WeakMap object still exists. It removes entry containing the object once it is garbage collected. For each private field, we need to create a new WeakMap object.

Following snippet shows how WeakMaps can be used as private fields:


const ID = new WeakMap();
const PAN = new WeakMap();

class Employee{
  constructor(id, name, pan){
    this.name = name;
    ID.set(this, id);
    PAN.set(this, pan);
  }
  getPan(){
    return PAN.get(this);
  }
}
var emp = new Employee(1001, "Ravi", "FFHH1919JJ");
console.log(emp.getPan());

export default Employee;


As you see, we are exporting the Employee class alone from the module. Here, the classes don't contain the fields themselves, but they are relying on the private objects of the module to keep the data private.

Happy Coding!

4 comments:

  1. Nice and simple way to achieve private fields in es6.

    ReplyDelete
  2. nice post! but really.. why do they go to the trouble to create a class paradigm and leave half of it out.

    ReplyDelete
    Replies
    1. If they did a full implementation, the spec for classes would have never been approved. There is a reason the termed it "maximally_minimal_classes". The plan was to just get something in for now and improve on it in later iterations.

      Delete