Where is the problem in the first place, and then what is the solution? Let’s dive in.
Imagine you are declaring a constructor. Imagine for a second that we are in ES5 world (that we, unfortunately, have to use if we want our control add-ins to be fully compatible with all platforms on which both NAV and Business Central are supported).
This is my code:
Obviously, this shows 42, which is my current age.
Soon, it will be my birthday, so this would show 43:
Now, while I would absolutely love to be able to do something like this in real life:
As obvious as it is that we can’t declare anything as private directly, there are still things we can use. One beautiful concept that comes in handy is called closures. Closures are explained at length on a million of blogs, documentation sites, and code examples all over the internet, and you can google the bejesus out of them at your own pace, so I won’t delve into explaining closures here. I’ll simply jump right into applying them to solving the encapsulation problem.
There are at least two ways how you could handle this. Let’s first do the more obvious one: an access function.
Imagine the world without properties where you cannot do object.property = value (like vjeko.age = 25 in our case, as much as I’d totally love it!). In that world, you’d have getter and setter functions:
(ignore for a moment the fact that I am still using this.age to “encapsulate” the “property”)
Obviously, you’d be able to call these like this:
Then, if you wanted to have age as read-only, you’d simply drop the setAge setter function. If this.age was really private (which it isn’t), this would do the trick for you. The problem is, nothing defined on this is private, it’s accessible to anybody having access to any instance of the object. It’s as public as it gets.
To fix this problem, the first thing we need to do is move the getter function declaration from the prototype to the instance. Prototype members are closest to what we would call static in C#, even though in runtime they have certain behavioral traits of both static and instance members. However, let’s first move the member away from the prototype, and onto instance:
This solves only the first part of the problem, the fact that getAge was defined on the prototype rather than on the instance. However, with a simple change like this:
… we solve the problem entirely:
Age is now fully encapsulated. You can access it through the getter function, but you cannot set it directly because it’s only accessible in the closure scope of the this.getAge instance function.
To fully implement the Person class, you need to move the growOlder function from the prototype into the instance:
And this works exactly as you want it to work:
But why does it work? It works because of closures. The age parameter from the constructor was captured in the closure scope of both getAge and growOlder functions, allowing you to access its value from both of these functions, but making it completely inaccessible to anybody else, anywhere else.
Even better solution
I addressed the Object.defineProperty method in my previous post, and if you read that one, you can immediately see how it can be applied here.
So, let’s define a read-only instance property of Person class:
There. It didn’t hurt. And it works: