Private properties in es6 classes (js)

references:

  • https://stackoverflow.com/questions/22156326/private-properties-in-javascript-es6-classes
  • https://www.javascriptjanuary.com/blog/es6-classes
  • https://ttmm.io/tech/private-variables-in-javascript/
  • https://esdiscuss.org/topic/es6-problem-with-private-name-objects-syntax

ECMAScript 2015 (aka ES6) introduced classes to JavaScript.

Older versions of the language would allow you to define an object type directly using function constructors, but didn’t truly support object-oriented inheritance the way developers are used to.

Part of this is due to the fact that JavaScript is a prototypal language, not an object-oriented language. Or at least older versions weren’t.

ES6 gave us classes with proper constructors, methods, properties, and inheritance.

You could define a real class in ES6 and reuse it the same way you would a type in any other language:

The biggest unfortunate element of this class definition, though, is that there are no privacy modifiers.

The constructor, methods, and properties of a JS class are all public by default.

There’s no nature of a protected method or a private property. At all.

Module Privacy

The name variable inside this module is completely inaccessible to any code outside the module, yet it allows our class to keep track of this name property directly. This is exactly what we want, right?

Nope.

Since it’s outside the class definition, this variable is treated as a static property. Yes, it’s private to our module, but there’s only one copy available. If we make multiple instances of our Client above, we’ll overwrite name each time; the last instantiation will win and define the name used by all of the instances.

Hack

We create a static literal object called container. When an instance of Client is created, we give the instance an id, then use the instance id
to create a key in the container object. Its corresponding value would be an empty object where we store the name variable.

Thus, each instance at least can keep track of its own properties and usage.

Not attaching the new properties to the object, but keeping them inside a class constructor

Hiding with Weak Maps

https://javascript.info/map-set-weakmap-weakset
https://stackoverflow.com/questions/22156326/private-properties-in-javascript-es6-classes

WeakMaps associate data with Objects (here, instances) in such a way that it can only be accessed using that WeakMap. So, we use the scoped variables method to create a private WeakMap, then use that WeakMap to retrieve private data associated with this. This is faster than the scoped variables method because all your instances can share a single WeakMap, so you don’t need to recreate methods just to make them access their own WeakMaps.

WeakMap, another way

https://chrisrng.svbtle.com/using-weakmap-for-private-properties

WeakMap Technique
Traditionally, the way to have private properties in Javascript is to either prefix your variables or to encapsulate in a closure. Both of these methods do work, however they are either not restrictive enough (prefixes) or too restrictive (closures).

A WeakMap is similar to a HashMap but it allows objects as keys as well as not having a strong reference to its values (that’s why it’s called weak).

In the example below, we pass in an empty object into the WeakMap to hold all of the private properties of the class Wizard. To store the private properties, we also pass in the reference to the unique this object of the instance of the class Wizard as a key to the WeakMap.

When we actually run this code, you can see on the second line that when we try to access the property _private the class returned does not have reference to it because of the IIFE. However internal class methods can still change the values under _private. So you can only change the properties within the Class by using its accessor methods such as get and set. In doing so, keeping the namespace hidden from all functions except members of the class effectively implements private properties.

Break Down

ref – https://stackoverflow.com/questions/22156326/private-properties-in-javascript-es6-classes

The only truly private data in JavaScript is still scoped variables. You can’t have private properties in the sense of properties accessed internally the same way as public properties, but you can use scoped variables to store private data.

Scoped variables

The approach here is to use the scope of the constructor function, which is private, to store private data.

For methods to have access to this private data they must be created within the constructor as well, meaning you’re recreating them with every instance.

This is a performance and memory penalty, but some believe the penalty is acceptable. The penalty can be avoided for methods that do not need access to private data by adding them to the prototype as usual.

Example:

Scoped WeakMap

A WeakMap can be used to avoid the previous approach’s performance and memory penalty.

WeakMaps associate data with Objects (here, instances) in such a way that it can only be accessed using that WeakMap. So, we use the scoped variables method to create a private WeakMap, then use that WeakMap to retrieve private data associated with this. This is faster than the scoped variables method because all your instances can share a single WeakMap, so you don’t need to recreate methods just to make them access their own WeakMaps.

Example:

This example uses an Object to use one WeakMap for multiple private properties; you could also use multiple WeakMaps and use them like age.set(this, 20), or write a small wrapper and use it another way, like privateProps.set(this, ‘age’, 0).

The privacy of this approach could theoretically be breached by tampering with the global WeakMap object

Say you’re coding up a JS app and you import another file written by Fred for usage.
Then in your current file, you create your class and then use WeakMap to store your private variables.

index.js


output:
I am Private1 I am Private2

However, say Fred is a bit naughty and decide to overwrite the global object Weakmap like so:

base64Coder.js

So Fred decides to repoint the set reference in Weakmap’s prototype to his own custom function. But before doing so, in order not to lose the original function, he’ll retain it with a reference called oldSet.

He then has access to your key/values and can potentially change them, or even store them somewhere else. When he’s done, he’ll just return the original function, pointed to by referenceoldSet.

That said, all JavaScript can be broken by mangled globals. Our code is already built on the assumption that this isn’t happening.

Solution to fix this

In order to remedy this, you first declare references to the original set/get functions on the Weakmap prototype.
This ensures you have access to them. Now, when the tamperer decides to inject their own function, what they’re doing is re-pointing Weakmap.prototype.set to their own evil function as shown above. Thus, you don’t have to worry about that because you have already set your own reference to the correct original functions like so:

The way how it used is like so. The first object is the object you’re using to execute the function call. The next parameters get matched up with the function parmaeters.

Just make sure you do it at the top of your file before you import/require the questioning code components.

Half Answer – Scoped Symbols

A Symbol is a type of primitive value that can serve as a property name. You can use the scoped variable method to create a private Symbol, then store private data at this[mySymbol].

The privacy of this method can be breached using Object.getOwnPropertySymbols, but is somewhat awkward to do.

Example:

For example, this is how you would expose it:

Half-Answer: Underscores

The old default, just use a public property with an underscore prefix. Though not a private property in any way, this convention is prevalent enough that it does a good job communicating that readers should treat the property as private, which often gets the job done. In exchange for this lapse, we get an approach that’s easier to read, easier to type, and faster.

Example: