ES6 Private Class Field Workarounds


Published: 2018-01-19
Updated: 2018-01-20
Web: http://fritzthecat-blog.blogspot.com/2018/01/es6-private-class-fields-workarounds.html


ES6: ?

ES6 classes provide no privacy in the sense of access modifiers. In other words, encapsulation lacks. The ES6 tutorial proposes four ways to achieve privacy, from which one is not real, because it is just the convention to use _underscore as prefix for private variables. Which would not prevent corruption in any way.

The other three ways, in my opinion, are actually not usable in real world. This Blog presents them in examples, and tries to show up their costs, so that you can decide this on your own.

Test Scripts

Click onto one of the left-side buttons to see an example script and its description. Below you find an output area for console.log() messages. You can also edit the script and execute it again by using the "Execute Script" button.

Description
Output


Resume

ES6 encapsulation is strongly connected to modules, there you can have invisible functions outside of an exported class. The restriction is that these must receive all their information via parameters, because they are not connected to any object-instance, they are like Java static.




This is definitely the best, safest and most general solution if you absolutely want ES6 privacy for any price.

The class CountDown defines a private variable counter in its constructor. This variable exists just in the block-scope of the constructor, but it survives the constructor execution due to functions increment(), decrement() and getValue() referencing it. But it is not accessible by any class-method outside the constructor! Mind that this counter can not be a class-property, because it would be public then by default.

The ugly thing about this solution is that all class-methods that deal with private fields have to be implemented inside the constructor body, and all private fields also have to be there. Thus we lose the elegant ES6 syntax for class methods. Because most public methods will want to call private ones, or use private fields, everything, the whole class body, has to go inside the constructor!

The nice thing about this solution is that you can also define private functions inside the constructor body. Just don't put them onto the this pointer, do it like reset().

This is a solution that works just when your class is encapsulated in an ES6 module.

For each private field of the class you need one WeakMap. The constructor, or any setter, stores the value of a field into the according map, using this as key. Any getter would read it from the map the same way.
We could regard this as an hidden internal instance management, to be stereotyped in every class that needs privacy.

Extremly ugly is the necessity to have one map for each field. Further you can not have private methods with this solution.

Why is the instance-management done with WeakMap? When the class instance, which is the map-key, wouldn't be referenced any more, it and all its field values would disappear by garbage-collection. A normal Map ould never release the object!

This is also a solution that works just when your class is encapsulated in an ES6 module.

For each private field of the class you need one Symbol value. The constructor, or any setter, stores the value of a field into a symbol-property onto the this object. Getters read it the same way. Although it is public, no outside code can read the property, because it can not know the symbol.
(Except when it reads the symbols by reflection via Object.getOwnPropertySymbols(person), but it could not guess the property's semantic, by no comparison like if (symbol === "firstName") ....)


ɔ⃝ Fritz Ritzberger, 2018-01-19