Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
Javascript state encapsulation without classes in 2019 (with private fields!)
I tend to avoid classes whenever possible in Javascript as I usually prefer a functional style over an object oriented one. But one thing that objects do really well is state encapsulation.
In this article, I’m going to argue for using the function constructor pattern without this as the preferred approach for encapsulating state. This was inspired by the widespread criticism of the private fields TC39 proposal on Reddit.
I’ll be talking about how to encapsulate state:
- With the ES2015 class keyword,
- With simple functions and object literals,
- Without using this and having private fields
At the end of the article, I have a bonus waiting for you, demonstrating composition with this pattern. You might like this if you like React Hooks!
Using ES2015Â classes
This is probably the most popular option as of 2019, and I think the following should be pretty easy to understand:
class MyObj1 { constructor(initVal) { this.myVal = initVal }
set(x) { this.myVal = x }}
Once instantiated:
const x = new MyObj1(0)
You can set the value of myVal with either:
x.set(2)
Or:
x.myVal = 2
Similarly, you can access the field directly:
console.log(x.myVal) // output: 2
This all seems great and the syntax looks very inviting to people familiar with other OOP languages, but there are significant flaws with this approach.
No private fields
The next time that your object field is something you didn’t expect, it’s not so easy to figure out where, when, and how it was changed.
It’s relatively straight-forward, but the lack of private fields make this implementation potentially dangerous. The whole point of state encapsulation is that you want to control what can and cannot alter the state (and how they do it).
But in this case, anyone (or any part of the program) can set x.myVal to anything. The next time that myVal is something you didn’t expect, it’s not so easy to figure out where, when, and how it was changed.
If you had private fields, however, you can have control over the behaviour of setting the variable via the set method on your object.
Extra mental overhead and API surface area
Understanding of the ES2015 class syntax is extra mental overhead for developers. There are on-going proposals to augment this with public and private fields (the syntax of which is still contentious and uncertain).
These proposals make it even more difficult for beginners and even seasoned developers (who don’t actively track TC39 proposals) to use classes in practice.
Classical inheritance
On top of that, the nature of the syntax (via the extends keyword) encourages inheritance over composition and incorrectly suggests the traditional classical inheritance pattern.
Some prior art regarding class and classical inheritance issues with Javascript:
- 10 Interview Questions Every Javascript Developer Should Know by Eric Elliott
- Composition Over Inheritance by FunFunFunction
- See also Douglas Crockford’s talk linked at the bottom of this article
Using simple functions and objects
i.e. “class-free” object oriented programming
So if we’re not using the class keyword, what should we be using?
Simple functions and objects! These are the basic building blocks that even the most junior developers are familiar with.
All we’re doing is writing a function that returns an object literal; not exactly rocket science.
This is how we declare our function:
const MyObj2 = initVal => { return { myVal: initVal, set: function(x) { this.myVal = x } }}
Notice that you didn’t need to learn about class or constructor at all. It’s just a function that returns an object. We create one like this:
const x = MyObj2(0)
We didn’t even need to use the new keyword because we’re not going to be modifying any prototypes (which is a bit of a code smell in 2019, IMHO). It’s simply a function that returns an object.
Similar to the ES2015 class approach, we can set the value with:
x.set(2)
Or:
x.myVal = 2
This approach still has all the issues of not having private fields, but at least we’re no longer in a different paradigm. I argue that this itself is a big improvement over the ES2015 class approach because there really is nothing new to learn here.
It’s just basic Javascript: Functions and Object Literals
Not using the `this`Â keyword
The this keyword in Javascript confuses both new and old programmers alike. In older code, you might see this being assigned to self or that. And a quick Google search will reveal numerous articles attempting to explain how the this keyword works in Javascript.
I’m not going to discuss why avoiding the this keyword is preferable, that is an exercise left to the reader since much has already been written about it. Instead, allow me to explain the benefits of an alternate approach.
If we simply use closures, we can define an object-creating function like this:
const MyObj3 = initVal => { let myVal = initVal return { get: function() { return myVal }, set: function(val) { myVal = val } }}
Just like the approach above, we can use it to create an object like this:
const x = MyObj3(0)
The myVal variable is essentially private, which means we can no longer access it using x.myVal like in the other implementations above. Instead we have to call the getter function:
x.get()
In some ways, this is a drawback of this approach, but it can also be considered a benefit when you consider the power and explicitness that it gives you.
Closures enabling a strong contract
The variable is conceptually “saved” inside the object returned from the function thanks to Javascript closures. This means that we can set the value with the setter method (i.e. x.set(2)), but attempting to directly change the field (i.e. x.myVal = 2) won’t do anything.
The greatest part of having these explicit setters and getters is that the object now has a strong contract/interface with the outside world. The state is fully encapsulated and the only ways in and out of the object is through the setters and getters.
What about prototypes?
Note that I completely skipped over using prototypes and prototypal inheritance in this conversation. This was deliberate, because modifying prototypes as a pattern has become less and less common in recent years.
And even Douglas Crockford (someone who was previously a proponent of prototypal inheritance) is now recommending “class-free” object oriented programming (i.e. the pattern discussed above).
What about performance?
One drawback about using closures is the issue of performance. While there doesn’t seem to be any difference when creating the objects, method calls on the object using closures were about 80% slower.
Admittedly, I didn’t look into this too much, but since the different results are in the same order of magnitude, it might be a case of premature optimization to be too concerned about this.
If you are making millions of method calls per second and performance really matters to you, then it’s a judgement call as to whether or not the sacrifice is worth it for your specific use-case.
Simplicity matters
Personally, I think a smaller API surface area is a good thing for the community as it means less confusion for beginners and more standardized patterns for getting things done.
You don’t have to use all features of a language, and you don’t get extra brownie points for doing so. Sometimes, less is more.
Why add classes when simple functions and plain Javascript objects will do? An understanding of closures is already required when you’re working with functions in Javascript. For more on this approach, check out Douglas Crockford’s talk here:
He adds Object.freeze for further safety, which I would recommend as well. The entire talk is worth watching, but pay special attention to the part where he starts talking about classes at 41:55Â onwards.
As we go forward in 2019, let’s commit to thinking critically about how we code and the language features we are using. Sometimes it might benefit us to use less rather than more.
For the full code, check out the repo here:
adrianmcli/js-state-encapsulation
Bonus: A counter example with composition!
Warning!!! This might remind you of React Hooks :)
Javascript state encapsulation without classes in 2019 was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.